Webpack原理简单讲解

Webpack原理简单讲解

webpack是JavaScript应用程序的静态模块打包器,webpack处理应用程序时,将递归构建一个依赖关系图,依赖图映射了项目中需要的每一个模块,并打包生成一个或多个bundle.

当在命令行运行指令 webpack 的时候,webpack将默认从当前目录下查找 webpack.config.js 文件

webpack的配置(webpack.config.js)中有四个核心概念需要理解:

  • 入口(entry)
  • 输出(output)
  • loader
  • 插件(plugins)

入口(entry)

entry属性将告诉webpack从哪个模块开始构建依赖图,并计算出所有这个模块直接或间接依赖的模块.

entry为String或Array时,entry输出的Chunk的名称将默认是main
entry为Object时,webpack将输出多个Chunk,Chunk的名称将会是key

点击此处查看详细内容

输出(output)

output属性将告诉webpack最终打包文件的输出路径,以及如何命名打包文件.

const path = require('path');

module.exports = {
  entry: './src/index.js',
  // ./dist/main.js
  output: {
    // 文件名字可以直接设置
    filename: 'my-first-webpack.bundle.js', 
    // 也可以用过变量设置
    // id - Chunk的唯一标识,从0开始
    // name - Chunk的名称
    // hash - Chunk的唯一标识(即id)的hash值
    // chunkhash - Chunk内容的hash值
    filename: '[id].[name].[hash].[chunkhash].js',
    // 配置输出文件的存放目录
    // __dirname是运行命令行时所在路径
    path: path.resolve(__dirname, 'dist'), 
  }
};
复制代码

node中的path,点击此处
点击此处查看详细内容

loader

module属性配置如何处理模块,其中rules配置模块的读取和解析规则,通常用来配置Loader.

未增加自定义配置的情况下,webpack只能处理JavaScript文件和JSON文件,Loader可以使webpack处理其他类型的文件到模块中,添加到依赖图中,被应用程序使用.

条件匹配: 通过 test / include / excluede 三个配置来匹配文件,支持string和array

应用规则: 匹配命中文件之后,使用use中的配置来应用loader,同时可以按"从后往前"的顺序应用多个loader

重置顺序: 一组loader的执行顺序迷人是从右向左执行,通过 enforce 选项可以让其中一个loader的执行顺序放到最后或最前

const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js'
  },
  module: {
    rules: [ // 在rules中可以设置多个规则
      {
        test: /\.txt$/, // 设置用来匹配什么文件需要被转换
        // 只命中src目录里的js文件,加快 Webpack 搜索速度
        include: path.resolve(__dirname, 'src'),
        // 排除 node_modules 目录下的文件
        exclude: path.resolve(__dirname, 'node_modules'),
        // 处理顺序为从后到前,即先交给 sass-loader 处理,再把结果交给 css-loader 最后再给 style-loader。
        use: ['style-loader', 'css-loader', 'sass-loader'],
        // use中同样可以设置Object
        use: [
              {
                loader:'babel-loader',
                options:{
                  cacheDirectory:true,
                },
                // enforce:'post' 的含义是把该 Loader 的执行顺序放到最后
                // enforce 的值还可以是 pre,代表把 Loader 的执行顺序放到最前面
                enforce:'post'
              },
        ]
      }
    ]
  }
};
复制代码

点击此处查看详细内容

插件(plugins)

loaders用来转换某些类型的模块(文件),plugin 则用来执行,打包优化,资源管理和插入环境变量,等各种任务

plugin 是用来扩展 Webpack 功能的,通过在构建流程里注入钩子实现,它给 webpack 带来了很大的灵活性。

通过require方法引入插件,并将其实例化的对象加入plugins中

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins

module.exports = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};
复制代码

上方例子, html-webpack-plugin 插件会给应用生成一个HTML文件,并在文件中插入打包好的文件.

tip: 使用 Plugin 的难点在于掌握 Plugin 本身提供的配置项,而不是如何在 Webpack 中接入 Plugin。

点击此处查看详细内容

webpack打包的基本逻辑

  1. 初始化参数

从配置文件和Shell语句中读取并合并参数,得出最终的参数

  1. 开始编译

用第一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象中的run方法开始执行编译

  1. 确认入口

根据配置中的entry找出所有的入口文件

  1. 编译模块

从入口文件出发,调用所有配置的loader对模块进行翻译,再找出该模块依赖的模块,递归查找所有的模块依赖

  1. 完成模块编译

经过第四部使用loader对所有模块进行翻译后,得到每个模块被翻译后的最终内容以及他们之间的依赖关系

  1. 输出资源

根据入口和模块之间的依赖关系,组装成一个个包含多个模块的Chunk,再把每个chunk转换成一个单独的文件并加入到输出列表中

  1. 输出完成

确认好输出内容后,根据配置确认输出的路径和文件名,把文件内容写入到文件系统中

tip: 上述过程中,webpack会在特定时间广播出特定时间,插件接受到想要的事件广播之后便会执行特性的逻辑,并且插件可以调用webpack提供的API改变webpack的运行结果

输出文件分析

webpack配置文件

const path = require('path')
// Since webpack 4 the "extract-text-webpack-plugin" should not be used for css.
// Use "mini-css-extract-plugin" instead
// const ExtractTextPlugin = require('extract-text-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const merge = require('webpack-merge')

// 这个在 npm run dev 和 npm run build 时候是不同的
const TARGET = process.env.npm_lifecycle_event
const APP_PATH = path.join(__dirname, '/src')
const dist = path.resolve(__dirname, 'dist')
const common = {
  entry: `${APP_PATH}/index.js`,
  output: {
    path: dist,
    filename: 'index.js'
  }
}

let other = {}

if (TARGET === 'dev') {
  other = {
    mode: 'development',
    module: {
      rules: [
        {
          test: /\.css$/,
          // TODO 理解loader的执行顺序
          use: [
            'style-loader', // Adds CSS to the DOM by injecting a