模块
CommonJS
CommonJS 规范概述了同步声明依赖的模块定义。这个规范主要用于在服务器端实现模块化代码组 织,但也可用于定义在浏览器中使用的模块依赖。CommonJS 模块语法不能在浏览器中直接运行。 注意 一般认为,Node.js的模块系统使用了CommonJS规范,实际上并不完全正确。Node.js 使用了轻微修改版本的 CommonJS,因为 Node.js 主要在服务器环境下使用,所以不需要 考虑网络延迟问题。考虑到一致性,本节使用 Node.js 风格的模块定义语法。 CommonJS 模块定义需要使用 require()指定依赖,而使用 exports 对象定义自己的公共 API。 下面的代码展示了简单的模块定义:
在 CommonJS 规范中,模块加载和执行是同步的。也就是说,当一个模块被加载时,它会立即执行并返回一个对象,该对象包含了该模块所定义的函数、变量等内容。在这个过程中,如果该模块还依赖其他模块,则会在加载这些模块后再执行该模块的代码。因此 require()可以像下面这样以编程 方式嵌入在模块中:
AMD
CommonJS 以服务器端为目标环境,能够一次性把所有模块都加载到内存,而异步模块定义(AMD, Asynchronous Module Definition)的模块定义系统则以浏览器为目标执行环境,这需要考虑网络延迟的问题。AMD 的一般策略是让模块声明自己的依赖,而运行在浏览器中的模块系统会按需获取依赖,并 在依赖加载完成后立即执行依赖它们的模块。
在 AMD 规范中,模块加载和执行是异步的。也就是说,当一个模块被加载时,它不会立即执行,而是先等待其他模块加载完毕后再执行。这种异步的模块加载和执行方式适用于浏览器环境,因为在浏览器环境中,文件的读取和加载速度比较慢,而且模块之间的依赖关系比较复杂,可能会出现循环依赖等问题。因此,AMD 规范引入了异步加载机制,能够更好地处理这些情况。
UMD
了统一 CommonJS 和 AMD 生态系统,通用模块定义(UMD,Universal Module Definition)规范 应运而生。UMD 可用于创建这两个系统都可以使用的模块代码。本质上,UMD 定义的模块会在启动时 检测要使用哪个模块系统,然后进行适当配置,并把所有逻辑包装在一个立即调用的函数表达式(IIFE) 中。虽然这种组合并不完美,但在很多场景下足以实现两个生态的共存。
使用ES6模块
与传统脚本不同,所有模块都会像<script defer>
加载的脚本一样按顺序执行。解析到<script type="module">
标签后会立即下载模块文件,但执行会延迟到文档解析完成。无论对嵌入的模块代码, 还是引入的外部模块文件,都是这样。<script type="module">
在页面中出现的顺序就是它们执行 的顺序。与<script defer>
一样,修改模块标签的位置,无论是在<head>
还是在<body>
中,只会影 响文件什么时候加载,而不会影响模块什么时候加载。 下面演示了嵌入模块代码的执行顺序:
也可以给模块标签添加 async 属性。这样影响就是双重的:不仅模块执行顺序不再与<script>
标 签在页面中的顺序绑定,模块也不会等待文档完成解析才执行。不过,入口模块仍必须等待其依赖加载 完成。 与<script type="module">
标签关联的 ES6 模块被认为是模块图中的入口模块。一个页面上有多少个入口模块没有限制,重复加载同一个模块也没有限制。同一个模块无论在一个页面中被加载多少次,也不管它是如何加载的,实际上都只会加载一次,如下面的代码所示:
嵌入的模块定义代码不能使用 import 加载到其他模块。只有通过外部文件加载的模块才可以使用 import 加载。因此,嵌入模块只适合作为入口模块。
模块加载
ECMAScript6模块这个过程与 AMD 风格的模块加载非常相似。模块文件按需加载,且后续模块的请求会因为每个依 赖模块的网络延迟而同步延迟。即,如果 moduleA 依赖 moduleB,moduleB 依赖 moduleC。浏览器在 对 moduleB 的请求完成之前并不知道要请求 moduleC。这种加载方式效率很高,也不需要外部工具, 但加载大型应用程序的深度依赖图可能要花费很长时间。
模块行为
ECMAScript 6 模块借用了 CommonJS 和 AMD 的很多优秀特性。下面简单列举一些。
模块代码只在加载后执行。
模块只能加载一次。
模块是单例。
模块可以定义公共接口,其他模块可以基于这个公共接口观察和交互。
模块可以请求加载其他模块。
支持循环依赖。 ES6 模块系统也增加了一些新行为。
ES6 模块默认在严格模式下执行。
ES6 模块不共享全局命名空间。
模块顶级 this 的值是 undefined(常规脚本中是 window)。
模块中的 var 声明不会添加到 window 对象。
ES6 模块是异步加载和执行的。
浏览器运行时在知道应该把某个文件当成模块时,会有条件地按照上述 ECMAScript 6 模块行为来施 15 加限制。与<script type="module">
关联或者通过 import 语句加载的 JavaScript 文件会被认定为模块。
向后兼容
Last updated