分析webpack源码的执行

12/18/2021 Source CodeWebpack
  • 我们要看的是 webpack底层工作的工具方法
  1. 新建项目,安装webpack,webpack-cli,之后用webpack进行打包。我们分析打包出来的bundle.js

# bundle.js

  • 我们模块中的代码经过 webpack 打包之后,就被放在了一个自执行函数中
  • 这个自调用函数接收一个参数,它的值是一个对象,这个对象的键就是相对于当前项目来说, 被打包文件的路径(moduleId),它的值就是我们被打包模块中的源代码,这个函数定义的 时候接收了二个参数 module exports
  • 我们在写代码的时候会使用 ESmodule 或者 commonjs 规范的 module,而 webpack 都可以处理,因为它的内部自己实现了很多自定义方法(__webpack_require__ 类似这种)

# bundle.js 流程

以单模块打包,且这个模块中没有其它的导包操作为例:

# 外部分析

  1. 将所有的内容都放置于一个自调用函数中,然后将被打包模块相关信息进行传参
  2. 相关信息就是一个对象,格式就是 moduleId: 组装后的函数( 函数体就是打包前的源码 )

# 内部函数体

  1. 自调用函数体内定义一个空对象用于存储缓存
  2. 自定义一个 __webpack_require__ 函数,它接收一个 ModuleId
  3. 这个 moduleId 是在自调用函数体的最后一行调用时传入的,100%就是入口文件的ID
  4. 自调用函数逻辑:
  • 判断当前 ModuleID 对应的模块是否存在于缓存中
  • 如果缓存中不存在的情况下,就自定义的一个 module 存放一个对象
  • 同时还将这个 对象存放在了 installModules[moduleId] 缓存里
  • 这个对象有三个属性:
    • i(存放当前被加载模块的 moduleId)
    • l(用于标记当前模块是否已加载, true 就是加载过,false就是没有加载过)
    • exports={}(最有用的,打包后的代码都是存在它里面返回)

# 调用

  • 通过 Modules[moduleId] 找到最初组装的那个函数(看2) 以 call 的方式来调用
    • 首先修改了this指向
    • 然后传入了 module (之前没有缓存定义的module,module里面本身就有exports) 和 module.exports (为啥这么干也是同时考虑到ESModules和CommonJS)
    • 最后还有 一个 webpack_require ,为了将来应对被打包模块中还有其它的模块导入(为了递归调用的)
  • 余下的就是常见工具定义的方法...

# 常见工具方法的作用

  • o: 可以判断当前对象中是否存在某个指定的属性
  • d: 给对象的某个属性添加一个 getter
  • r: 将对象标记为 __esModule
  • t: 太恶了放在最后看
  • n: 依据不同的模块返回一个返回模块内容的 getter

# r方法内容补充

Symbol.toStringTag

如何判断一个对象类型

console.log(Object.prototype.toString.call(12))
// [object Number]
console.log(Object.prototype.toString.call('12'))
// [object String]

let obj1 = {}
let obj2 = {}

console.log(Object.prototype.toString.call(obj1)) // [object Object]
console.log(Object.prototype.toString.call(obj2)) // [object Object]

// 我们现在就在手写 r 方法 
function r (object) {
  if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
    Object.defineProperty(object, Symbol.toStringTag, { 
      enumerable: true, 
      value: 'Module' 
    });
  }
  Object.defineProperty(object, '__esModule', { value: true });
}

console.log(obj1) // {}
r(obj1)
console.log(obj1) 
// Module {__esModule: true, Symbol(Symbol.toStringTag): "Module"}  浏览器执行(ESModule)
// { [Symbol(Symbol.toStringTag)]: 'Module' } node执行(CommonJS)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
更新时间: 2021-12-18 20:56