如何在浏览器和 Node 之中加载 ES6 模块
实际开发中经常遇到的一些问题(比如循环加载)
1.1.ES6 模块与 CommonJS 模块的差异
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
1.1.1.第一个差异解释
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
// main.js
var mod = require('./lib');
console.log(mod.counter); // 3
mod.incCounter();
console.log(mod.counter); // 3
遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。因此,ES6 模块是动态引用,并且不会缓存值。
1.1.2.第二个差异解释
因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。
而 ES6 模块不是对象,它的对外接口只是一种静态定义。
1.1.3.export通过构造函数实例化对象,输出的是同一个值。不同的脚本加载这个接口,得到的都是同样的实例。
// mod.js
function C() {
this.sum = 0;
this.add = function () {
this.sum += 1;
};
this.show = function () {
console.log(this.sum);
};
}
export let c = new C();
//上面的脚本mod.js,输出的是一个C的实例。
//不同的脚本加载这个模块,得到的都是同一个实例。
// x.js
import {c} from './mod';
c.add();
// y.js
import {c} from './mod';
c.show();
// main.js
import './x';
import './y';
//现在执行main.js,输出的是1。
1.2.浏览器加载
默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。
<script>标签打开defer或async属性,脚本就会异步加载。defer是“渲染完再执行”:defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async是“下载完就执行”:async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。
浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。
浏览器对于带有type="module"的<script>,都是异步加载,不会造成堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了<script>标签的defer属性。
如果网页有多个<script type="module">,它们会按照在页面出现的顺序依次执行。
一旦使用了async属性,<script type="module">就不会按照在页面出现的顺序执行,而是只要该模块加载完成,就执行该模块。
1.3.Node加载
Node 加载
详解
1.4.循环加载
Node 加载
详解
网友评论