ES Module的原理
ES Module和CommonJS的区别
一、CommonJS模块加载js文件的过程是运行时加载的,并且是同步的:
- 运行时加载意味着是js引擎在执行js代码的过程中加载 模块;
- 同步的就意味着一个文件没有加载结束之前,后面的代码都不会执行;
console.log("main代码执行");
const flag = true;
if (flag) {
// 同步加载foo文件,并且执行一次内部的代码
const foo = require('./foo');
console.log("if语句继续执行");
}
二、CommonJS通过module.exports导出的是一个对象:
- 导出的是一个对象意味着可以将这个对象的引用在其他模块中赋值给其他变量;
- 但是最终他们指向的都是同一个对象,那么一个变量修改了对象的属性,所有的地方都会被修改;
三、ES Module加载js文件的过程是编译(解析)时加载的,并且是异步的:
-
编译时(解析)时加载,意味着import不能和运行时相关的内容放在一起使用:
-
- 比如from后面的路径需要动态获取;
- 比如不能将import放到if等语句的代码块中;
- 所以我们有时候也称ES Module是静态解析的,而不是动态或者运行时解析的;
-
异步的意味着:JS引擎在遇到
import
时会去获取这个js文件,但是这个获取的过程是异步的,并不会阻塞主线程继续执行; -
- 也就是说设置了
type=module
的代码,相当于在script标签上也加上了async
属性; - 如果我们后面有普通的script标签以及对应的代码,那么ES Module对应的js文件和代码不会阻塞它们的执行;
- 也就是说设置了
<script src="main.js" type="module"></script>
<!-- 这个js文件的代码不会被阻塞执行 -->
<script src="index.js"></script>
四、ES Module通过export导出的是变量本身的引用:
- export在导出一个变量时,js引擎会解析这个语法,并且创建模块环境记录(module environment record);
- 模块环境记录会和变量进行
绑定
(binding),并且这个绑定是实时的; - 而在导入的地方,我们是可以实时的获取到绑定的最新值的;
所以我们下面的代码是成立的:
bar.js
文件中修改
let name = 'coderwhy';
setTimeout(() => {
name = "湖人总冠军";
}, 1000);
setTimeout(() => {
console.log(name);
}, 2000);
export {
name
}
main.js
文件中获取
import { name } from './modules/bar.js';
console.log(name);
// bar中修改, main中验证
setTimeout(() => {
console.log(name);
}, 2000);
但是,下面的代码是不成立的:main.js
中修改
import { name } from './modules/bar.js';
console.log(name);
// main中修改, bar中验证
setTimeout(() => {
name = 'kobe';
}, 1000);
思考:如果bar.js中导出的是一个对象,那么main.js中是否可以修改对象中的属性呢?
- 答案是可以的,因为他们指向同一块内存空间;(自己编写代码验证,这里不再给出)
- 因为他们指向同一块内存空间;(自己编写代码验证,这里不再给出)
原文链接:彻底掌握前端模块化