Webpack

Webpack

webpack 流程

  1. webpack 使用 Tapable 作为事件中心,Tapable 可以定义、监听、触发事件
  2. webpack 将打包分成了 initialize -> run -> compiler -> compilation -> make -> afterCompile -> emit -> done 阶段
  3. webpack 在开始的时候就创建 Compiler 实例 compiler,将 webpack.config.js 的 options 赋值给 compiler.options
  4. 之后经历从 environment 到 initialize 阶段,之后调用 compiler 的 run 方法
  5. 在 compiler.compile 方法会创建 NormalModuleFactory + ContextModuleFactory 实例
  6. 在 compiler.compile 阶段创建 Compilation 实例
  7. EntryPlugin 监听了 make 事件,获取到入口文件路径,调用 compilation.addEntry,将路径传递给 compilation
  8. ------- compilation 拿到入口文件之后开始工作 ----------
  9. 广播 compilation addEntry 事件
  10. compilation 调用 NormalModuleFactory.create 方法,创建 module
  11. module 是 NormalModule 的实例,调用 module.build 方法,开始 build
  12. ------- module 是 NormalModule 的实例,多态,调用到 NormalModule.build 方法 ----------
  13. ------- parse ----------
  14. NormalModule.build 方法调用了 runLoaders,使用 loader 去解析文件,获取到 Buffer 数组,处理之后就可以拿到文件内容,赋值给 normal._source
  15. 使用 parser 去解析文件内容 => NormalModule 中的 parser 是 JavaScriptParser 的实例,所以调用 JavaScriptParser.parse 解析
  16. JavaScriptParser 使用了 acorn 第三方库去解析 code 到 ast
  17. traverse ast,得到所有的 import 语句,一旦发现 importDeclaration 则触发 JavaScriptParser.hooks.import 钩子,对应的监听函数就会处理依赖
  18. ------- 得到了 _source(源代码) 和 ast -----------
  19. ------- DFS process dependencies => module chain(compilation.modules) -------------
  20. 之后 DFS 处理 module 的 dependencies,所有的 module 形成一个链,此时 module 全部创建完成
  21. module chain 是一个数组,挂载在 compilation.modules 属性上,是一个 Set
  22. 广播 compilation succeedEntry 事件
  23. ------ compilation 完成事情 => compilation 根据入口文件获取到所有的 module ----------
  24. ------ EntryPlugin 调用的方法 compilation.addEntry 完成 -----------------
  25. ------ 进入下一个阶段 ------------------
  26. 广播 compiler.finishMake 事件
  27. ------ code generator => compilation.assets ----------
  28. 调用 compilation.seal 方法,将 compilation.modules 遍历生成一个 CacheSource
  29. 将所有的要输出的代码放置在 CacheSource._children 中
  30. 此时的 cacheSource 是一个数组,内容就是打包后的 main.js
  31. 将上述的 CacheSource 实例挂载到 compilation.assets['main.js']._source 上
  32. 广播 compiler.afterCompile 事件
  33. ----- 此时完成了 compiler.compile 方法执行 ------------
  34. 调用 compiler.emitAssets 方法触发 compiler.emit 事件
  35. 在 compiler.emit 事件回调中创建 dist 文件夹
  36. 之后调用 emitFiles 创建文件,文件来自于 compilation.assets
  37. 之后调用 doWrite 进行具体地写文件操作,之后触发 compiler.assetEmitted 事件
  38. ----- 完成了 compiler.emitAssets 方法,进入回调 ----
  39. 调用 compiler.emitRecords 方法
  40. compiler.emitRecords 方法回调中触发 compiler.done 事件

概述

AST

  • AST => Abstract Syntax Tree => 抽象语法树

Babel

babel 原理

  1. parse => 把代码 code 变成 AST
  2. traverse => 遍历 AST 进行修改
  3. generate => 把 AST 变成代码 code2
  4. code --1--> ast --2--> ast2 --3--> code2
let to var
  1. 使用 @babel/core 和 @babel/preset-env 可以将代码转为 ES5

将转化后的代码输出到一个文件中

读取文件(test.js),并将文件中的代码转化(file_to_es5.ts)之后写入另一个文件(test.es5.js)

工具

  1. babel 可以把高级代码翻译成 ES5
  2. @babel/parser
  3. @babel/traverse
  4. @babel/generator
  5. @babel/core 包含前三者
  6. @babel/preset-env 内置很多规则

依赖

分析 JS 文件的依赖关系

  1. deps_1.ts + project_1
  2. node -4 ts-node/register deps_1.ts

递归的分析嵌套依赖

  1. 依赖关系
  2. DFS
  3. deps_2.ts + project_2

静态分析循环依赖

  1. deps_3.ts + project_3
  2. node -r ts-node/register deps_3.ts
  3. Error: 栈溢出

解决循环依赖

  1. deps_4.ts + project_4
  2. 如果分析过了,就直接返回
  3. 静态分析 => 只分析依赖,不执行代码
  4. node project_4/index.js => 执行代码 => ReferenceError: Cannot access 'a' before initialization

总结

  1. 模块间可以循环依赖
  2. 代码不能有逻辑漏洞
  3. 没有逻辑漏洞的循环依赖 => project_5
  4. 最佳实践 => 不要使用循环依赖

打包 bundle

  1. 浏览器不能直接运行 ES6+ 代码
  2. 浏览器功能不同
    1. 现代浏览器可以通过

你可能感兴趣的:(Webpack)