1. 前言
在开始阅读本文前,建议先阅读下面的两篇文章:
2. 依赖关系的动态与静态
CommonJS是==动态的==: 模块间的依赖关系是在代码运行阶段确定的
ES6 Module是==静态的==: 模块间的依赖关系是在代码编译阶段确定的
二者的这一点不同,显示出的ES6 Module的优点如下:
- ES6 Module在编译阶段就能确定出模块间的依赖关系,可以排除掉未引用的模块,减小打包资源的体积
- 静态结构可以保证模块间值传递的类型正确(因为在编译阶段就会检查)
- CommonJS本质是导入module对象,而ES6 Module导入的是只读变量,减少层级引用编译,效率更高
3. 模块导入的不同
CommonJS导入的模块是原来模块的拷贝,任何操作都不会对原拷贝有影响;而ES6 Module导入的模块是原来模块的只读引用,虽然是只读引用,但是依然可以设计出通过函数方法对原模块的改变:
// Sam.js
export default {
name : 'sam',
age : 25,
getAge: function() {
return this.age++
}
}
// index.js
import Sam from "./Sam.js";
for (let i = 0; i < 3; i++) {
console.log(Sam.getAge());
}
// 控制台输出
// 25
// 26
// 27
需要注意的是,用ES6 Module导出的变量不可以直接修改,否则会抛出只读错误。
import count from './calculator' // 假设count是被导出导入的一个数字
// count++ // SyntaxError: "count" is read-only
4. 循环引用
这部分我理解还不够深刻,只是简单地记住概念,暂时先做部分记录,欢迎交流
如果使用CommonJS,模块间的循环引用会出现空值,因为它是脚本式地执行下去,如果执行到某一行当时的值未定义,就会获取到类似空的对象等(因为webpack有做判断为空时创建一个空对象{}
);
而ES6 Module则会在编译时先把各个模块间关系先生成,再声明对应的变量和函数方法等,利用这个特点,可以跟上一节一样,创建出可以循环引用且不为空的结果,方式参考第三点中的方式,其中CommonJS也可以通过如下方式实现引用:
下面来展示第一种情况,当循环引用时,如果使用CommonJS会怎么样:
// index.js
1 > require('./A.js')
// A.js
2 > const B = require('./B.js')
3 > console.log('value of B: ', B)
4 > module.exports = 'This is A !'
// B.js
5 > const A = require('./A.js')
6 > console.log('value of A:', A)
7 > module.exports = 'This is B !'
// 控制台
// value of A: {}
// value of B: this is B !
- 首先会执行第1行index.js中的代码,然后跳转到A.js中执行2的代码,再跳转到B.js中执行5的代码
- 此时不会再跳回A,而是往下执行6、7的代码。因为是动态执行,此时还没读到4,所以此时的A会因为webpack的处理,被赋予
{}
,依旧是打印内容为空的对象。 - 执行完6、 7,B.js中的代码执行完毕,回到A.js中,执行3,因为B.js的导出已经被度去过,所以打印了出来,然后再执行4导出文本,代码执行完毕。
内容均为我个人阅读《Webpack实战:入门、进阶与调优》后的回忆总结,这是一本很不错的书,想深入了解webpack的同学可以去看看这本书