Open dave-wind opened 1 year ago
1.拿到所有的 options 2. 创建 Compiler 对象 进行webpack 初始化 // compiler.hooks.run 3. 执行run 方法,开启编译,编译过程会创建 一个compilation对象 每次编译都会变化 5.compiler 创建只会产生一次, compilation负责整个编译过程 compilation上有: (代表的就是当前的编译构建) 创建模块 普通模块加载; 递归所依赖的模块加载; 解析模块resolve modules 所有模块(创建所有模块) chunks 代码块 assets 所有的资源 // 当前编译的所有的资源 4. make阶段 分析入口文件 对每个模块进行编译;递归依赖模块 放到一个module变量上 5. 解析文件内容 loadRunner 生成AST 遍历AST;解析模块 resolve ; parse ast解析 语法 call调用(require)表达式 6. compilation 打包阶段 seal 方法 逐次对module chunk 进行整理 合并和拆分 7.模版编译 template模版 8. 最后 发射流程 输出output
compiler.hooks 方法: done : 编译完成 run: 编译器开始读取 之前执行 shouldEmit: 输出asset之前 我没可以做 js 的处理 正则js文件 umd 替换 // 可以理解为 shouldEmit发射前 可以通过tap事件流 拿到 compilation资源 包含解析的chunk 和 assets compiler.hooks.shouldEmit.tap(pluginName, (compilation) => { for (const chunk of compilation.chunks) { // compilation.assets[file] // js css // 正则 匹配 /\.js$/.test(file) // 正则替换资源 source.replace } } emit: 文件提交到dist目录前 compilation: 创建 compilation beforeCompile 在编译前 compile: 创建compilation前 make 编译 在最后阶段,每个依赖项都会成为一个模块实例 最后 tap类 就是事件流 一般分 同步和异步 NormalModuleFactory: // 模块生成各类模块 // 对模块进行请求 分析 compiler.hooks.normalModuleFactory.tap(xxx,(nmf)=>{ // 匹配csr! 进行替换。主要是npm打包 一般npm都是客户端执行 // eg: import videoPlayer from "csr!video-player" ===>. import videoPlayer from "video-player" nmf.hooks.beforeResolve.tap(‘’, (result)=>{ // result.request 对模块 解析阶段 if(/^csr!/.test(result.request)) { var str = result.request.match(/^csr!([\s\S]+)/)[1] // 返回数组项 result.request = str } }) // umd包 情况 需要 外面加 window !== undefined 判断 避免服务端渲染报错 nmf.hooks.afterResolve.tap("NormalModuleReplacementPlugin", (result) => { const { dependencies } = result; // 对依赖进行解析 找到匹配项 dependencies.some(item => /^csr!/.test(item.request)) // plugin 模块其实可以拿到 loader 数组 只要给最后的loader 增加自定义loader 就可以解决问题 result.createData.loaders = [...result.createData.loaders, path.resolve(__dirname, "xxxcsrChange.js")] } }) // xxxcsrChange.js 如下 在该自定义loader里可以拿到 匹配的资源 进行 函数包层 判断window是否存在即可 module.exports = function(source) { if (!source) return source; const result = `if(typeof window !== undefined){(source)}` } 模块生成各类模块之后, 把资源进行拆分 解析各种资源模块: beforeresolve // 各个 模块的请求 resolve afterResolve // 在生成模块实例之前 解析之后 解析之后 可以拿到模块的依赖 还可以自定义loader 解析文件的时候 可以去匹配依赖性 匹配资源 然后继续处理
normalModuleFactory 解析模块 createData loaders 如下 还是从源码找到的
webpack 插件 可以调用底层api 改变编译结果 最简单的插件:
webpack 插件 可以调用底层api 改变编译结果
最简单的插件:
// 插件都会定义一个 apply方法 传入 cpmpiler 调用 其身上的钩子函数, cpmpiler是什么?
module.exports = class RunPlugin { apply(compiler) { // run 是什么? 原理 来源于 tabable 这个库 compiler.hooks.run.tap('Runplugin',()=>{ console.log("Run") }) }
// compiler 是webpack 的实例。 调用 插件 let options = require(‘./webpack.config.js’) let compiler = new Cpmpiler(options) } // webpack options 里 plugins 是数组 for (const plugin of options.plugins) { //. 传入 webpack compiler 实例 plugin.apply(compiler) }
##### Loader > 就是加载器, 让webpack拥有加载和解析非javascript的文件能力,比如处理 img css ```js
Webpack
webpack 流程
plugins
// 插件都会定义一个 apply方法 传入 cpmpiler 调用 其身上的钩子函数, cpmpiler是什么?
module.exports = class RunPlugin { apply(compiler) { // run 是什么? 原理 来源于 tabable 这个库 compiler.hooks.run.tap('Runplugin',()=>{ console.log("Run") }) }
// compiler 是webpack 的实例。 调用 插件 let options = require(‘./webpack.config.js’) let compiler = new Cpmpiler(options) } // webpack options 里 plugins 是数组 for (const plugin of options.plugins) { //. 传入 webpack compiler 实例 plugin.apply(compiler) }