webpack 4 源码主流程分析(三):编译前的准备

Compiler 简述

  • webpack/lib/Compiler.js 该文件是 webpack 的核心, Compiler 类定义了整个构建的流程;

  • new Compiler 执行 constructor,首先扩展了 Tapable,在 constructor 里定义了一堆钩子 done,beforeRun,run,emit 等等;

  • 然后注册了this._pluginCompat.tap("Compiler"),这个用来兼容之前的老版 webpack 的 plugin 的钩子,触发时机在tapable/lib/Tapable.js里调用plugin 的时候;

  • 该 Compiler 类下的的 run 即为整个打包的主流程函数;

封装 FS

继续执行 webpack.js,执行:

newNodeEnvironmentPlugin({
  infrastructureLogging: options.infrastructureLogging,
}).apply(compiler);
复制代码

该类主要对文件系统做了一些封装,包括输入,输出,缓存,监听等等,这些扩展后的方法全部挂载在 compiler 对象下。

执行 plugins

项目配置的 plugins

然后对自己 config 文件里的 plugins 进行了注册:

if (options.plugins && Array.isArray(options.plugins)) {
  for (const plugin of options.plugins) {
    if (typeof plugin === 'function') {
      plugin.call(compiler, compiler);
    } else {
      plugin.apply(compiler);
    }
  }
}
复制代码

在这里,会把 compiler 实例传进去供 plugin 使用,compiler 包含整个构建流程的全部钩子,通过它可以把控整个 webpack 构建周期。其中 compiler 的部分钩子会传入 compilation 对象参数,该对象又包含资源构建的很多钩子。

掌握流程里各对象(如 compiler,compilation)的事件钩子触发的时机,就是掌握如何写一个插件的关键。如何写一个 webpack 插件?

接着触发了 compiler 的 hooks: environment,afterEnvironment,然后执行:

compiler.options = newWebpackOptionsApply().process(options, compiler);
复制代码

项目默认的 plugins

该 WebpackOptionsApply 类的 process 除了把配置里的一些属性添加到 compiler 对象下,更主要的是根据 options 的配置不同,注册激活一些默认自带的插件和 resolverFactory.hooks,大部分插件的作用是往 compiler.hooks:compilation,thisCompilation 里注册一些事件(此时该钩子已经获取到 normalModuleFactory 等参数),如:

newJavascriptModulesPlugin().apply(compiler); //给normalModuleFactory的js模块提供Parser、JavascriptGenerator对象 ,并给seal阶段的template提供renderManifest数组(包含render方法)newJsonModulesPlugin().apply(compiler); //给normalModuleFactory的json模块提供Parser、JavascriptGenerator对象newWebAssemblyModulesPlugin({
  mangleImports: options.optimization.mangleWasmImports,
}).apply(compiler); // 同理,webassembly模块复制代码
newEntryOptionPlugin().apply(compiler);
compiler.hooks.entryOption.call(options.context, options.entry); //创建多入口还是单入口 SingleEntryPlugin | MultiEntryPlugin,两者均会在 apply 方法里注册 Compiler.hooks:compilation, make复制代码

插件处理完毕后,触发 compiler.hooks: afterPlugins。

注册 resolverFactory.hooks

compiler.resolverFactory.hooks.resolveOptions.for('normal').tap('WebpackOptionsApply', (resolveOptions) => {
  returnObject.assign(
    {
      fileSystem: compiler.inputFileSystem,
    },
    cachedCleverMerge(options.resolve, resolveOptions) //配置项 options.resolve
  );
});
compiler.resolverFactory.hooks.resolveOptions.for('context').tap('WebpackOptionsApply', (resolveOptions) => {
  returnObject.assign(
    {
      fileSystem: compiler.inputFileSystem,
      resolveToContext: true,
    },
    cachedCleverMerge(options.resolve, resolveOptions) //配置项 options.resolve
  );
});
compiler.resolverFactory.hooks.resolveOptions.for('loader').tap('WebpackOptionsApply', (resolveOptions) => {
  returnObject.assign(
    {
      fileSystem: compiler.inputFileSystem,
    },
    cachedCleverMerge(options.resolveLoader, resolveOptions) //配置项 options.resolveLoader
  );
});
复制代码

然后注册 compiler.resolverFactory.hooks: resolveOptions for (normal/context/loader),目的是为 Factory.createResolver 提供默认的参数对象(含有相关的 resolve 项目配置项)。

注册完成后,触发 compiler.hooks: afterResolvers,到此 compiler 初始化完毕。

回到 cli.js

回到cli.js,处理配置项 progress 和 infoVerbosity,然后判断 options 里是否有 watch,有则走 compiler.watch,无则走 compiler.run,这里我们走compiler.run,进入 webpack 核心构建流程!

本章小结

  1. 实例化了 Compiler,它扩展于Tapable,是 webapck 的核心;

  1. 封装了输入输出等方法 FS ,然后执行了 plugins(compiler 作为参数),包括项目配置的和项目默认;

  1. 注册 resolverFactory.hooks 用于 Factory.createResolver 方法提供参数对象;

  1. 最后根据配置是否有 watch 来决定程序走向。

作者:Korey

链接:https://juejin.cn/post/6844904047225339911

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值