深入理解 ES Module

2022-09-29 19:26:02 浏览数 (1)

语法基本介绍

Module是ES6 出现的一个新的语法,提供一种将 JavaScript 程序拆分为可按需导入的单独模块的机制。在未出现之前,我们可能使用 commonjs等模块规范。随着ES Module的普及和推广,浏览器已经支持原生的模块规范。

语法也比较简单,一般来说使用可以分为以下几个步骤:

  • 使用export导出一个模块(更加详细的语法支持参照 javascript modules[1], Module的加载实现[2])
代码语言:javascript复制
//foo.js
function foo() {
    return "export foo";
}
console.log("foo.loading");

export { foo };
  • 使用import 可以导入一个模块
代码语言:javascript复制
//main.js
import { foo } from "./foo.js";

console.log(foo());
  • 应用模块到html
代码语言:javascript复制
//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存在循环加载的例子:

代码语言:javascript复制
//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)函数时要保证已存在。最终输出结果如下:

代码语言:javascript复制
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

0 人点赞