dave-wind / blog

native javascript blog
0 stars 0 forks source link

webpack 知识点 #13

Open dave-wind opened 1 year ago

dave-wind commented 1 year ago

Webpack

webpack 流程

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 如下 还是从源码找到的

image

plugins

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