webpack运行过程(1)(webpack流程)
合并配置项
实例化Compiler
加载所有插件
runWebpack() // webpack-cli/lib/webpack-cli.js createCompiler() // webpack-cli/lib/webpack-cli.js webpack() // webpack/lib/webpack.js create() //webpack/lib/webpack.js var compiler = createCompiler(webpackOptions) // webpack/lib/webpack.js const createCompiler = rawOptions => { // 合并配置项,即将在webpack.config.js中配置的和webpack自带的配置合并 const options = getNormalizeWebpackOptions(rawOptions) // 实例化compiler const compiler = new Compiler(options.context, options) // 遍历插件,执行插件的apply()方法 if(Array.isArray(options.plugins)){ for(const plugin of options.plugins) { if(typeof plugin === "function"){ plugin.call(compiler, compiler) }else{ plugin.apply(compiler) } } } // 实例化WebpackOptionsApply,并执行它的process()方法 new WebpackOptionsApply().process(options, compiler) return compiler } 复制代码
加载 入口插件EntryPlugin
// 调用WebpackOptionsApply //此处的options是我们在webpack.config.js中配置的内容 new WebpackOptionsApply().process(options, compiler) // webpack/lib/webpack.js // 调用EntryOptionPlugin插件 process(){ // webpack/lib/WebpackOptionsApply.js ... new EntryOptionPlugin().apply(compiler) compiler.hooks.entryOption.call(options.context, options.entry) ... } // EntryOptionsPlugin插件的apply方法 apply(compiler){ compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => { EntryOptionPlugin.applyEntryOption(compiler, context, entry) return true }) } // applyEntryOption是EntryOptionPlugin类的静态方法 static applyEntryOption(compiler, context, entry){ if(typeof entry === "function"){ }else{ const EntryPlugin = require("./EntryPlugin"); // 遍历入口对象的key for(const name of Object.keys(entry)){ const desc = entry[name] ... for(const entry of desc.import){ new EntryPlugin(context, entry, options).apply(compiler) } } } } // webpack/lib/EntryPlugin.js // 在EntryPlugin插件的apply()方法中我们调用了compiler的make钩子 // make钩子: compilation结束之前执行 apply(compiler){ ... const { entry, options, context } = this ... compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => { }) } 复制代码
实例化Compilation, 根据入口的数量,1次或多次调用该实例对象的addEntry()方法, addEntry()主要是将entry放入到AsyncQueue中
compiler.run() //webpack/lib/Compiler.js compiler.compile() //webpack/lib/Compiler.js compiler.newCompilationParams() //webpack/lib/Compiler.js compiler.createNormalModuleFactory() //webpack/lib/Compiler.js const normalModuleFactory = new NormalModuleFactory() // constructor() //构造函数,webpack/lib/NormalModuleFactory.js const compilation = compiler.newCompilation() //webpack/lib/Compiler.js // 调用compiler.hooks.make.callAsync即是执行compiler.hooks.make.tapAsync // compiler.hooks.make.callAsync只会执行一次 // compiler.hooks.make.tapAsync 内部执行次数 等于 入口的个数 compiler.hooks.make.callAsync(compilation, err=>{}) //webpack/lib/Compiler.js compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback)=>{ }) // webpack/lib/EntryPlugin.js // 以下是EntryPlugin插件apply()方法中 apply(compiler){ ... const { entry, options, context } = this ... compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => { // 有n个入口,这里就会被执行n次 compilation.addEntry(context, dep, options, err => { callback(err) }) }) } 复制代码
简化版
class Compilation { constructor(){ } addEntry(entry){ console.log(entry) } } const compilation = new Compilation() compilation.addEntry('app.js') compilation.addEntry('search.js') 复制代码
实例化一个异步队列(AsyncQueue),存储一个entry; 多个entry对应多个异步队列
//webpack/lib/Compiler.js compilation.addEntry() compilation._addEntryItem() //webpack/lib/Compilation.js compilation.addModuleTree() // webpack/lib/Compilation.js compilation.handleModuleCreation() // webpack/lib/Compilation.js compilation.factorizeModule() //webpack/lib/Compilation.js compilation.prototype.factorizeQueue = new AsyncQueue({}) //webpack/lib/Compilation.js Compilation.factorizeQueue.add() //webpack/lib/Compilation.js AsyncQueue.add() //webpack/lib/util/AsyncQueue.js 复制代码
宏任务执行AsyncQueue的_ensureProcessing()方法,该方法内会遍历前面加入到AsyncAueue中的所有entry,做后续操作,未完待续,期待下期的 webpack运行过程(2)
// 在执行宏任务之前会将多个entry加入到AsyncQueue的_children中 // 宏任务执行root._ensureProcessing setImmediate(root._ensureProcessing) _ensureProcessing() //webpack/lib/util/AsyncQueue.js // 这里的_children可以理解为多个入口 // 遍历多个入口 for(const child of this._children){ // _parallelism为并发数 while(this._activeTasks < this._parallelism){ const entry = child._queued.dequeue(); if(entry === undefined) break; this._activeTasks++ entry.state = PROCESSING_STATE child._startProcessing(entry) } } _startProcessing() //webpack/lib/util/AsyncQueue.js 复制代码
上述简化版实现
class Compilation { constructor(){ } addEntry(entry){ const asyncAueue = new AsyncQueue() asyncAueue.add(entry) } } class AsyncQueue{ constructor(){ this.list = [] } add(entry){ this.list.push(entry) // 这里要使用bind指定this,不然this会执行执行调用者setImmediate setImmediate(this._ensureProcessing.bind(this)) } _ensureProcessing(){ for (let index = 0; index < this.list.length; index++) { const entry = this.list[index]; this._startProcessing(entry) } } _startProcessing(entry){ console.log(entry) } } const compilation = new Compilation() compilation.addEntry('app.js') compilation.addEntry('search.js')
作者:yanessa_yu
链接:https://juejin.cn/post/7031457009979359263