调试webpack过程了解执行流程
开始-合并配置------------实例化compile-------设置node文件读写能力-----通过循环挂载plugins-----处理webpack内部默认的插件(入口文件) 开始-compiler.beforeRun-compiler.run--------compiler.beforeCompile-compiler.compile-------compile.make
在
Compiler
类中,构造函数内会挂载大量的钩子,这些钩子都来自tapable
,挂载之后在后续的操作中,这些钩子等待被触发执行。this.hooks = {
/** @type {SyncBailHook} */
shouldEmit: new SyncBailHook(["compilation"]),
/** @type {AsyncSeriesHook} */
done: new AsyncSeriesHook(["stats"]),
/** @type {AsyncSeriesHook<>} */
additionalPass: new AsyncSeriesHook([]),
/** @type {AsyncSeriesHook} */
beforeRun: new AsyncSeriesHook(["compiler"]),
/** @type {AsyncSeriesHook} */
run: new AsyncSeriesHook(["compiler"]),
/** @type {AsyncSeriesHook} */
emit: new AsyncSeriesHook(["compilation"]),
/** @type {AsyncSeriesHook} */
assetEmitted: new AsyncSeriesHook(["file", "content"]),
/** @type {AsyncSeriesHook} */
afterEmit: new AsyncSeriesHook(["compilation"]),/** @type {SyncHook} */
thisCompilation: new SyncHook(["compilation", "params"]),
/** @type {SyncHook} */
compilation: new SyncHook(["compilation", "params"]),
/** @type {SyncHook} */
normalModuleFactory: new SyncHook(["normalModuleFactory"]),
/** @type {SyncHook}*/
contextModuleFactory: new SyncHook(["contextModulefactory"]),/** @type {AsyncSeriesHook} */
beforeCompile: new AsyncSeriesHook(["params"]),
/** @type {SyncHook} */
compile: new SyncHook(["params"]),
/** @type {AsyncParallelHook} */
make: new AsyncParallelHook(["compilation"]),
/** @type {AsyncSeriesHook} */
afterCompile: new AsyncSeriesHook(["compilation"]),/** @type {AsyncSeriesHook} */
watchRun: new AsyncSeriesHook(["compiler"]),
/** @type {SyncHook} */
failed: new SyncHook(["error"]),
/** @type {SyncHook} */
invalid: new SyncHook(["filename", "changeTime"]),
/** @type {SyncHook} */
watchClose: new SyncHook([]),/** @type {SyncBailHook} */
infrastructureLog: new SyncBailHook(["origin", "type", "args"]),// TODO the following hooks are weirdly located here
// TODO move them for webpack 5
/** @type {SyncHook} */
environment: new SyncHook([]),
/** @type {SyncHook} */
afterEnvironment: new SyncHook([]),
/** @type {SyncHook} */
afterPlugins: new SyncHook(["compiler"]),
/** @type {SyncHook} */
afterResolvers: new SyncHook(["compiler"]),
/** @type {SyncBailHook} */
entryOption: new SyncBailHook(["context", "entry"])
};
实现迷你版webpack暂时不需要这么多钩子.
文件目录结构,lib文件夹下package.json中
"main": "./lib/webpack.js",
文章图片
NodeEnvironmentPlugin.js
文件主要给compiler挂载node读写文件功能const fs = require('fs')class NodeEnvironmentPlugin {
constructor(options) {
this.options = options || {}
}apply(complier) {
// 源码中还有处理日志的功能,这里暂不需要,这里只需要使compiler具备文件读写能力即可
complier.inputFileSystem = fs
complier.outputFileSystem = fs
}
}module.exports = NodeEnvironmentPlugin
Compiler.js
文件实现compiler实例化,挂载钩子,实现run
方法。const {
Tapable,
AsyncSeriesHook
} = require('tapable')class Compiler extends Tapable {
constructor(context) {
super()
this.context = context
// 源码中的钩子会有很多
this.hooks = {
done: new AsyncSeriesHook(["stats"])
}
}run(callback) {
callback(null, {
toJson() {
return {
entries: [], // 当前打包入口信息
chunks: [], // 当前打包代码块信息
modules: [], // 模块信息
assets: [], // 打包生成的资源
}
}
})
}
}module.exports = Compiler
webpack.js
实现webpack流程主要步骤const Compiler = require("./Compiler")
const NodeEnvironmentPlugin = require('./node/NodeEnvironmentPlugin')const webpack = function (options) {
// 实例化 compiler 对象
let compiler = new Compiler(options.context)
compiler.options = options
// 初始化 NodeEnvironmentPlugin(让compiler具体文件读写能力)
new NodeEnvironmentPlugin().apply(compiler)
// 挂载所有 plugins 插件至 compiler 对象身上
if (options.plugins && Array.isArray(options.plugins)) {
for (const plugin of options.plugins) {
plugin.apply(compiler)
}
}
// 挂载所有 webpack 内置的插件(入口)// 最后返回
return compiler}module.exports = webpack
测试代码
let webpack = require('./myPack')
let options = require('./webpack.config.js')let complier = webpack(options)complier.run((err, stats) => {
console.log(err)
console.log(stats.toJson({
entries: true,
chunks: false,
modules: false,
assets: false
}))
})
【迷你版webpack实现】运行后打印
{ entries: [], chunks: [], modules: [], assets: [] }