语法基本介绍
Module
是ES6 出现的一个新的语法,提供一种将 JavaScript 程序拆分为可按需导入的单独模块的机制。在未出现之前,我们可能使用 commonjs
等模块规范。随着ES Module
的普及和推广,浏览器已经支持原生的模块规范。
语法也比较简单,一般来说使用可以分为以下几个步骤:
- 使用
export
导出一个模块(更加详细的语法支持参照javascript modules
[1], Module的加载实现[2])
//foo.js
function foo() {
return "export foo";
}
console.log("foo.loading");
export { foo };
- 使用import 可以导入一个模块
//main.js
import { foo } from "./foo.js";
console.log(foo());
- 应用模块到html
//index.html
<script type="module" src="./main.js"></script>
ES 模块加载流程
在之前外链js 文件的时候,如果遇到 <script src="xxx.js">
会阻止DOM解析
,直到js 文件加载,执行结束之后继续再进行 DOM 解析。
ES Module文件的加载会有所不同。当使用 type=module
会默认加入 defer
属性。也就是说文件是进行异步加载的,等待DOM 解析结束之后才会执行。具体如下图:
循环加载
当两个模块相互引用,模块又是如何加载执行的呢,先看模块a
,模块b
存在循环加载的例子:
//index.html
<script type="module" src="src/a.js"></script>
//src/a.js
import { bar } from "./b.js";
console.log("a.mjs");
console.log(bar());
// let foo = "foo";
function foo() {
return "foo";
}
export { foo };
//src/b.js
import "./c.js";
import { foo } from "./a.js";
con
sole.log("b.mjs");
//src/c.js
console.log("hello world from c.js");
ES6 模块会分为预加载和执行阶段,在预处理阶段会分析模块依赖。在加载阶段会从入口文件实现深度优先遍历算法进行文件获取,获取.js 的顺序即是:a.js -> b.js -> c.js。在执行阶段是从子模块开始的,也即是,c.js -> b.js -> a.js。
ES6 模块导出的是值的引用,如果使用import
从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。也就是在 b.js 中执行 foo ( a.js)函数时要保证已存在。最终输出结果如下:
hello world from c.js
b.mjs
foo
a.mjs
bar
总结
从本文我们可以了解到:ES Module文件代码是异步加载的;ES Module导出的是值的引用。有一些实际的例子,也可自行尝试一下。
参考资料
[1 ] JavaScript modules:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Modules
[2] Module 的加载实现:https://wangdoc.com/es6/module-loader.html