webpack源码分析(六)
这一章我们先给一个demo,从demo的视角讲解,new WebpackOptionsApply().process(options, compiler);都发生了什么。
先上demo
首先第一步,把webpack源码clone下来, git clone https://github.com/webpack/webpack.git
然后执行yarn,把npm包下载下来,然后mkdir demo 创建一个demo文件夹,文件夹内部创建一个webpack.js文本就是下方的内容
var path = require("path"); var webpack = require("../lib/index").webpack; const config = { // mode: "development" || "production", entry: { main: "./example" }, output: { path: path.join(__dirname, "dist"), filename: "[name].chunkhash.js" } }; webpack(config,(err,stats)=>{ console.log(err,stats) })
然后node inspect webpack.js,然后打开浏览器f12,发现有一个node的小点(绿色的),然后点击开来,我们就可以调试了。
为什么我要讲解这里呢,还要写一个demo呢,因为不同的配置,对应的插件不一样,我不可能细到所有的都说出来,只能尽可能的全。
回到 new WebpackOptionsApply().process(options, compiler);
我们先看这两个参数,一个option,一个compiler,option是我们传入,然后经过webpack的两个方法处理过的值,compiler就是我们new compiler编译对象。
这块代码太多了,我就不讲了,后续用到,触发这个plugin(后续用到我在讲),我们说一下他做了哪些处理,1.绑定plguin事件
绑定了非常多的事件,2. 触发了几个事件
我们讲一下触发的事件
compiler.hooks.entryOption.call(options.context, options.entry);
compiler.hooks.afterPlugins.call(compiler);
compiler.hooks.afterResolvers.call(compiler);
entryOption
webpack 一共有两个钩子会触发这个事件,一个是EntryOptionPlugin,一个是DLLPlugin,第二个是webpack5中已经没人用了,他的作用是静态资源链接,打包一次,之后就不用在打包了,既然已经不是主流了,vue,react的脚手架都放弃了,那我们就不讨论了。
那我们就看EntryOptionPlugin
apply(compiler) { // 注册entryOption compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => { EntryOptionPlugin.applyEntryOption(compiler, context, entry); return true; }); }
EntryOptionPlugin.applyEntryOption(compiler, context, entry);
他就执行了这个,那我们看这个方法
static applyEntryOption(compiler, context, entry) { if (typeof entry === "function") { const DynamicEntryPlugin = require("./DynamicEntryPlugin"); new DynamicEntryPlugin(context, entry).apply(compiler); } else { const EntryPlugin = require("./EntryPlugin"); // {main:{import:['./example']}} for (const name of Object.keys(entry)) { // {import:['./example']} const desc = entry[name]; // name :main const options = EntryOptionPlugin.entryDescriptionToOptions( compiler, name, desc ); for (const entry of desc.import) { // context entry:'./example' options:{name:'main'} new EntryPlugin(context, entry, options).apply(compiler); } } } }
这段代码的关键就是EntryPlugin
apply(compiler) { // 给compilation 增加 dependencyFactories compiler.hooks.compilation.tap( "EntryPlugin", (compilation, { normalModuleFactory }) => { compilation.dependencyFactories.set( EntryDependency, normalModuleFactory ); } ); const { entry, options, context } = this; const dep = EntryPlugin.createDependency(entry, options); // 增加 make compiler.hooks.make.tapAsync("EntryPlugin", (compilation, callback) => { compilation.addEntry(context, dep, options, err => { callback(err); }); }); } static createDependency(entry, options) { const dep = new EntryDependency(entry); // TODO webpack 6 remove string option dep.loc = { name: typeof options === "object" ? options.name : options }; return dep; }
给 compilation.dependencyFactories set(EntryDependency, normalModuleFactory) 给一个map增加一个属性,这个normalModuleFactory挺关键,现在不讲,后续会抽一节讲
然后,创建一个dep,const dep = EntryPlugin.createDependency(entry, options); 注意 就是这个dep new EntryDependency,所以dep.constructor === EntryDependency,所以compilation.dependencyFactories.get(dep.construtor) ===normalModuleFactory 后续有用
所以entryOption就是给make钩子增加一个事件,事件执行,compilation.addEntry方法
afterPlugins
ModuleFederationPlugin模块联邦,默认也没有用,就先不讲了
afterResolvers
没有方法,就不说这个钩子了
回顾一下,WebpackOptionsApply().process,做了什么?
- 注册插件
- 执行三个函数
1. 包含常规前端面试题解析,和源码分析 2. 从0-1深入浅出理解前端相关技术栈