webpack5(五)模块化原理

515 阅读2分钟

在 webpack 打包应用程序时,你可以选择各种模块语法风格,包括 ES6,CommonJS 和 AMD。 通过查看源码了解webpack中是怎么实现最常用的ES Module、CommonJs和互相引用的

  • 查看源码前先设置mode和soucre map,让生成的源码容易阅读
module.exports = {
  mode: 'development',
  devtool: 'source-map',
  ....
}

CommonJs模块化原理

math.js

const sum = (num1, num2) => {
  return num1 + num2
}

module.exports = {
  sum
}

main.js

const { sum } = require('./js/math')

console.log(sum(10, 10))

npm run build

image.png

  1. 最外层是一个自执行函数,让每个文件形成各自作用域,避免互相污染

  2. 48行又是一个自执行函数,调用前面声明的__webpack_require__函数并传入文件路径

image.png

  1. __webpack_require__函数中首先查找缓存对象,没有缓存再通过__webpack_modules__对象去查找对应文件

image.png

  1. __webpack_modules__对象 原文件的路径作为key,原文件的内容作为value并且是函数。这就是模块化的核心代码,让每个模块形成单独作用域。 image.png

ES6 模块化原理

匿名导出

format.js

const getDay = (date) => {
  return date.getDay()
}

export default getDay 

main.js

import getDay from './js/format.js'

console.log(getDay(new Date()))

npm run build

image.png

  1. 第一个自执行函数就是给webpack_require函数定义了一个属性__webpack_require__.r
    • 如果支持Symbol给形参设置自定义标签,参考文末有解释
    • 给形参设置属性__esModuletrue,就是用来区分是不是ES6模块化

image.png

  1. 首先调用了上一步的__webpack_require__.r,然后调用__webpack_require__函数,传入文件路径

image.png

  1. 基本和commonJs处理一样,区分在于最终将原JS代码存在了module.exports.default属性上

image.png

image.png

具名导出

format.js

export const getDay = (date) => {
  return date.getDay()
}

main.js

import { getDay } from './js/format.js'

console.log(getDay(new Date()))

npm run build

image.png

  1. o函数用于判断对象是否包含某个属性,d函数给module.exports添加属性。这里定义时只设置了get拦截,没有set。所以不像commonjs在获取完导入模块还能更改模块中的变量

image.png

image.png

ES6导入CommonJs的兼容处理

image.png

image.png

ES6和CommonJs区别

  • CommonJs将导出变量通过对象保存,所以可更改
  • ES6import { sum } from './math',这里的大括号解构module.exports,其中每个属性只做了get监听,所以不支持修改。不使用{}就认为导入匿名模块,查找module.exports.default

format.js

export const getDay = (date) => {
  return date.getDay()
}

main.js

import getDay from './js/format.js'

console.log(getDay(new Date()))

npm run build

image.png 编译报错,可以看出通过ES6导入模块没有使用大括号{},会把被导入模块当作匿名导出去找default属性

备注

  1. !function(){}() 自执行函数第三种写法,通过运算符告诉js引擎这是一个表达式可以直接调用
  2. Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); // '[object Module]' 给对象设置自定义类型标签 参考链接