在 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
-
最外层是一个自执行函数,让每个文件形成各自作用域,避免互相污染
-
48行又是一个自执行函数,调用前面声明的__webpack_require__函数并传入文件路径
- __webpack_require__函数中首先查找缓存对象,没有缓存再通过__webpack_modules__对象去查找对应文件
- __webpack_modules__对象 原文件的路径作为key,原文件的内容作为value并且是函数。这就是模块化的核心代码,让每个模块形成单独作用域。
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
- 第一个自执行函数就是给webpack_require函数定义了一个属性
__webpack_require__.r
- 如果支持Symbol给形参设置自定义标签,参考文末有解释
- 给形参设置属性
__esModule
为true
,就是用来区分是不是ES6模块化
- 首先调用了上一步的
__webpack_require__.r
,然后调用__webpack_require__
函数,传入文件路径
- 基本和commonJs处理一样,区分在于最终将原JS代码存在了
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
- o函数用于判断对象是否包含某个属性,d函数给module.exports添加属性。这里定义时只设置了get拦截,没有set。所以不像commonjs在获取完导入模块还能更改模块中的变量
ES6导入CommonJs的兼容处理
ES6和CommonJs区别
- CommonJs将导出变量通过对象保存,所以可更改
- ES6
import { 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
编译报错,可以看出通过ES6导入模块没有使用大括号{},会把被导入模块当作匿名导出去找default属性
备注
!function(){}()
自执行函数第三种写法,通过运算符告诉js引擎这是一个表达式可以直接调用Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); // '[object Module]'
给对象设置自定义类型标签 参考链接