webpack打包流程

375 阅读5分钟

webpack打包流程概括

webpack的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

1. 初始化参数 从配置文件和 Shell 语句中读取与合并参数,得出最终的参数

2. 开始编译 用上一步得到的参数初始Compiler对象,加载所有配置的插件,通
过执行对象的run方法开始执行编译

3. 确定入口 根据配置中的 Entry 找出所有入口文件

4. 编译模块 从入口文件出发,调用所有配置的 Loader 对模块进行编译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理

5. 完成模块编译 在经过第4步使用 Loader 翻译完所有模块后, 得到了每个模块被编译后的最终内容及它们之间的依赖关系

6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再将每个 Chunk 转换成一个单独的文件加入输出列表中,这是可以修改输出内容的最后机会

7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,将文件的内容写入文件系统中

以上7个步骤,可以简单归纳为初始化、编译、输出,三个过程

简化打包代码


这里的moduleId就是模块路径,如./src/commonjs/index.js。webpack4中只有optimization.namedModules为true,此时moduleId才会为模块路径,否则是数字id

webpack支持commonjs和ES的打包,还支持两者混合使用,而es的导出有export,还有export default

Webpack Loader

Loader 就像一个翻译员,能将源文件经过转化后输出新的结果,并且一个文件还可以链式地经过多个翻译员翻译。

{
    test: /.less$/,
    use: [
    { loader: 'style-loader',},
    { loader: 'css-loader', },
    {
      loader: 'less-loader',
      options: {
        strictMath: true,
       },
    },
    ],
},

一个Loader 的职责是单一的,只需要完成一种转换

一个Loader 其实就是一个Node.js 模块,这个模块需要导出一个函数

如果一个源文件需要经历多步转换才能正常使用,就通过多个 Loader 去转换,在调用多个 Loader去转换一个文件时,每个 Loader 都会链式地顺序进行。第一个 Loader 将会拿到需处理的原内容,上一个 Loader 处理后的结果会被传给下一个 Loader 接着处理,最后的 Loader 将处理后的最终结果返回给webpack

Loader模型

source参数是compiler 传递给 Loader 的一个文件的原内容,这个函数需要返回处理后的内容,这里为了简单起见,直接将原内容返回了,相当于该Loader 有做任何转换.这里结合了webpack的api和第三方模块

Webpack Plugin

专注处理 webpack 在编译过程中的某个特定的任务的功能模块,可以称为插件

1. 是一个独立的模块

2. 模块对外暴露一个 js 函数

3. 函数的原型 (prototype) 上定义了一个注入 compiler 对象的 apply 方法

4. apply 函数中需要有通过 compiler 对象挂载的 webpack 事件钩子,钩子的回调中能拿到当前编译的 compilation 对象,如果是异步编译插件的话可以拿到回调 callback

5. 完成自定义子编译流程并处理 compilation  对象的内部数据

6. 如果异步编译插件的话,数据处理完成后执行 callback 回调

webpack 插件机制是整个 webpack 工具的骨架,而 webpack 本身也是利用这套插件机制构建出来的

Plugin基本模型

class BasicPlugin{
  //在构造函数中获取用户为该插件传入的配置
  constructor(pluginOptions){
      this.options=pluginOptions;
  }
  //原型定义一个apply函数,并注入了compiler对象
  apply(compiler){
      //挂载webpack事件钩子(这里挂载是emit事件)
      compiler.Plugin('emit',function(compilation.callback){
          //...内部进行自定义的编译操作
          //操作compilation对象的内部数据
          console.log(compilation);
          执行callback回调
          callback();
      })
  }
}
暴露js函数
module.exports=BasicPlugin

Webpack 启动后,在读取配置的过程中会先执行 new BasicPlugin(options )初始化一个 BasicPlugin 并获得其实例。在初始化 Compiler 对象后,再调用 basicPlugin.apply (compiler )为插件实例传入 compiler 对象。插件实例在获取到 compiler 对象后,就可以通过 compiler. plugin (事件名称 ,回调函数)监听到 Webpack 广播的事件,并且可以通过 compiler 对象去操作 Webpack。

Compiler对象

compiler 对象是 webpack 的编译器对象,compiler 对象会在启动 webpack 的时候被一次性地初始化,compiler 对象中包含了所有 webpack 可自定义操作的配置,例如 loader 的配置,plugin 的配置,entry 的配置等各种原始 webpack 配置等

else if (typeof options === "object") {
		options = new WebpackOptionsDefaulter().process(options);

		compiler = new Compiler(options.context);
		compiler.options = options;
		new NodeEnvironmentPlugin().apply(compiler);
		if (options.plugins && Array.isArray(options.plugins)) {
			for (const plugin of options.plugins) {
				plugin.apply(compiler);
			}
		}
webpack部分源码:https://github.com/webpack/webpack/blob/10282ea20648b465caec6448849f24fc34e1ba3e/lib/webpack.js#L30

初始化 webpack 各配置参数、初始化 compiler 对象、往 compiler 添加初始化参数、往 compiler 添加 Node 环境相关方法

Compilation 对象

1.compilation 实例继承于 compiler,compilation 对象代表了一次单一的版本 webpack 构建和生成编译资源的过程

2.当运行 webpack 开发环境中间件时,每当检测到一个文件变化,一次新的编译将被创建,从而生成一组新的编译资源以及新的 compilation 对象

3.一个 compilation 对象包含了 当前的模块资源、编译生成资源、变化的文件、以及 被跟踪依赖的状态信息。编译对象也提供了很多关键点回调供插件做自定义处理时选择使用

Compiler 和 Compilation 的区别在于: Compiler 代表了整个 Webpack 从启动到关闭的生命周期,而 Compilation 只代表一次新的编译。