【前端必会】tapable、hook,webpack的灵魂

背景

  1. 什么是tapable、hook,平时做vue开发时的webpack 配置一直都没弄懂,你也有这种情况吗?
  2. 还是看源码,闲来无聊又看一下webpack的源码,看看能否找到一些宝藏
  3. tapable和webpack没有特定关系,可以先看下这篇文章,了解下这个小型库https://webpack.docschina.org/api/plugins/#tapablehttps://blog.csdn.net/mafan121/article/details/1131200814.下面记录下寻宝过程
开始执行一次webpack经历了什么,先看一下代码
【前端必会】tapable、hook,webpack的灵魂

文章插图
我们分析一下4点
  1. 引用了webpack
  2. 我们使用的配置文件
  3. 调用webpack函数,传入配置,返回一个compiler(编译器)
  4. 执行编译器的run方法
分析引用webpack,先把这个函数找出来https://github.com/webpack/webpack/blob/main/package.json"main": "lib/index.js",
https://github.com/webpack/webpack/blob/main/lib/index.js
module.exports = mergeExports(fn, { get webpack() {return require("./webpack"); },https://github.com/webpack/webpack/blob/main/lib/webpack.js
const webpack = /** @type {WebpackFunctionSingle & WebpackFunctionMulti} */ ( /*** @param {WebpackOptions | (ReadonlyArray<WebpackOptions> & MultiCompilerOptions)} options options* @param {Callback<Stats> & Callback<MultiStats>=} callback callback* @returns {Compiler | MultiCompiler}*/ (options, callback) => {const create = () => {if (!asArray(options).every(webpackOptionsSchemaCheck)) {getValidateSchema()(webpackOptionsSchema, options);util.deprecate(() => {},"webpack bug: Pre-compiled schema reports error while real schema is happy. This has performance drawbacks.","DEP_WEBPACK_PRE_COMPILED_SCHEMA_INVALID")();}/** @type {MultiCompiler|Compiler} */let compiler;let watch = false;/** @type {WatchOptions|WatchOptions[]} */let watchOptions;if (Array.isArray(options)) {/** @type {MultiCompiler} */compiler = createMultiCompiler(options,/** @type {MultiCompilerOptions} */ (options));watch = options.some(options => options.watch);watchOptions = options.map(options => options.watchOptions || {});} else {const webpackOptions = /** @type {WebpackOptions} */ (options);/** @type {Compiler} */compiler = createCompiler(webpackOptions);watch = webpackOptions.watch;watchOptions = webpackOptions.watchOptions || {};}return { compiler, watch, watchOptions };};if (callback) {try {const { compiler, watch, watchOptions } = create();if (watch) {compiler.watch(watchOptions, callback);} else {compiler.run((err, stats) => {compiler.close(err2 => {callback(err || err2, stats);});});}return compiler;} catch (err) {process.nextTick(() => callback(err));return null;}} else {const { compiler, watch } = create();if (watch) {util.deprecate(() => {},"A 'callback' argument needs to be provided to the 'webpack(options, callback)' function when the 'watch' option is set. There is no way to handle the 'watch' option without a callback.","DEP_WEBPACK_WATCH_WITHOUT_CALLBACK")();}return compiler;} });module.exports = webpack;这里主要就是调用create创建一个Compiler(先不理watch)
【前端必会】tapable、hook,webpack的灵魂

文章插图
在看一下create,这里是调用的createCompiler或者createMultiCompiler
【前端必会】tapable、hook,webpack的灵魂

文章插图
【【前端必会】tapable、hook,webpack的灵魂】在看一下createCompiler,这里主要就是new一个Compiler 。这个时候已经开始了webpack编译的生命周期 。
/** * @param {WebpackOptions} rawOptions options object * @returns {Compiler} a compiler */const createCompiler = rawOptions => { const options = getNormalizedWebpackOptions(rawOptions); applyWebpackOptionsBaseDefaults(options); const compiler = new Compiler(options.context, options); new NodeEnvironmentPlugin({infrastructureLogging: options.infrastructureLogging }).apply(compiler); if (Array.isArray(options.plugins)) {for (const plugin of options.plugins) {if (typeof plugin === "function") {plugin.call(compiler, compiler);} else {plugin.apply(compiler);}} } applyWebpackOptionsDefaults(options); compiler.hooks.environment.call(); compiler.hooks.afterEnvironment.call(); new WebpackOptionsApply().process(options, compiler); compiler.hooks.initialize.call(); return compiler;};我们简单看一下Compiler类的一些hooks
const { SyncHook, SyncBailHook, AsyncParallelHook, AsyncSeriesHook} = require("tapable");......class Compiler { /*** @param {string} context the compilation path* @param {WebpackOptions} options options*/ constructor(context, options = /** @type {WebpackOptions} */ ({})) {this.hooks = Object.freeze({/** @type {SyncHook<[]>} */initialize: new SyncHook([]),/** @type {SyncBailHook<[Compilation], boolean>} */shouldEmit: new SyncBailHook(["compilation"]),/** @type {AsyncSeriesHook<[Stats]>} */done: new AsyncSeriesHook(["stats"]),/** @type {SyncHook<[Stats]>} */afterDone: new SyncHook(["stats"]),/** @type {AsyncSeriesHook<[]>} */additionalPass: new AsyncSeriesHook([]),/** @type {AsyncSeriesHook<[Compiler]>} */beforeRun: new AsyncSeriesHook(["compiler"]),/** @type {AsyncSeriesHook<[Compiler]>} */run: new AsyncSeriesHook(["compiler"]),/** @type {AsyncSeriesHook<[Compilation]>} */emit: new AsyncSeriesHook(["compilation"]),/** @type {AsyncSeriesHook<[string, AssetEmittedInfo]>} */assetEmitted: new AsyncSeriesHook(["file", "info"]),/** @type {AsyncSeriesHook<[Compilation]>} */afterEmit: new AsyncSeriesHook(["compilation"]),

推荐阅读