现在,所有主流浏览器都支持JavaScript modules

本文介绍了如何使用JS modules,如何负责任地部署它们以及Chrome团队如何努力使modules在将来变得更好。

什么是JS模块?

JS模块(也称为“ES模块”或“ECMAScript模块”)是一项主要的新功能,或者是新功能的集合。 您过去可能使用过userland JavaScript模块系统。 也许您在Node.js中使用过CommonJS,或者在AMD中使用过,或者在其他方面使用过。 所有这些模块系统都有一个共同点:它们允许您导入和导出内容。

JavaScript现在已经为此提供了标准化语法。 在模块内,您可以使用export关键字导出几乎所有内容。 您可以export constfunction或任何其他变量绑定或声明。 只要在变量语句或声明前加上export前缀,就可以设置好:

// 📁 lib.mjs
export const repeat = (string) => `${string} ${string}`;
export function shout(string) {
  return `${string.toUpperCase()}!`;
}

然后,您可以使用import关键字从另一个module导入module。在这里,我们从lib module导入repeatshout功能,并在我们的main module中使用它:

// 📁 main.mjs
import {repeat, shout} from './lib.mjs';
repeat('hello');
// → 'hello hello'
shout('Modules in action');
// → 'MODULES IN ACTION!'

您还可以从module导出default值:

// 📁 lib.mjs
export default function(string) {
  return `${string.toUpperCase()}!`;
}

可以使用任何名称导入此类default导出:

// 📁 main.mjs
import shout from './lib.mjs';
//     ^^^^^

Modules与classic scripts略有不同:

  • 默认情况下,模块具有严格模式
  • 模块中不支持HTML样式的注释语法,尽管它可以在classic scripts中使用。
    // Don’t use HTML-style comment syntax in JavaScript!
    const x = 42; <!-- TODO: Rename x to y.
    // Use a regular single-line comment instead:
    const x = 42; // TODO: Rename x to y.
  • Modules具有词汇top-level范围。 这意味着,例如,运行var foo = 42;尽管在classic scripts中就是这种情况,但在modules中创建一个名为foo的全局变量并不能通过浏览器中的window.foo进行访问。
  • 同样,modules内的this不引用全局this,而是undefined。(如果需要访问全局this,请使用globalThis。)
  • 新的静态importexport语法仅在modules内可用-在classic scripts中不起作用。
  • Top-level await在modules中可用,但在classic scripts中不可用。 相关地,尽管classic scripts中的变量可以在异步函数之外命名为await,但是await不能在Modules中的任何位置用作变量名。

由于存在这些差异,相同的JavaScript代码在作为module scripts与classic scripts时可能会表现出不同的行为。因此,JavaScript运行时需要知道哪些scripts是modules。

在浏览器中使用JS modules

在网络上,可以通过将type属性设置为module来告诉浏览器将<script>元素视为modules。

<script type="module" src="main.mjs"></script>
<script nomodule src="fallback.js"></script>

了解type="module"的浏览器将忽略具有nomodule属性的脚本。 这意味着您可以为支持modules的浏览器提供基于modules的有效负载,同时为其他浏览器提供备用。 即使只是为了表现,这种区分的能力也是惊人的! 考虑一下:只有现代的浏览器才支持modules。 如果浏览器了解您的modules代码,则它还支持modules之前的功能,例如箭头函数或async-await。 您不再需要在modules捆绑包中转换这些功能! 您可以为现代浏览器提供较小的,且未移植的,基于modules的有效负载。 只有旧版浏览器才能获取nomodule有效负载。

由于默认情况下modules是延迟的,因此您可能还希望以延迟的方式加载nomodule脚本:

<script type="module" src="main.mjs"></script>
<script nomodule defer src="fallback.js"></script>

JavaScript modules