目录#
简介#
本文翻译自作者 Mark Brown 的 Using ES Modules in the Browser Today
本文将向您展示如何在浏览器中使用ES Modules。
前言#
一段时间以前,Javascript 还没有模块的概念,这对于在一个 Javascript 文件里直接引入另一个 Javascript 文件的内容来说,这样的想法是无法实现的。而随着网页应用的复杂度和规模不断增长,为浏览器编写Javascript代码变得越来越棘手了。
此前的一个常见的解决办法是在网页用通过 script
标签来加载任意脚本,然而这个方法会带来其特有的麻烦。举例来说,每个脚本执行会阻塞 HTTP 请求,这就会导致使用大量Javascript脚本的网页会变得卡顿和反应迟钝;同时,依赖管理也变得复杂,因为脚本加载的顺序也很重要。
ES6 (ES2015) 通过引入一个单一的、标准化的本地模块解决了这个问题(你可以点此了解更多ES6 Modules的内容)。但由于早期浏览器对ES6模块的支持度不高,开发者们开始使用模块加载器来将依赖项捆绑到 ES5 跨浏览器兼容文件中,这个办法同样也有它的问题和复杂程度。
但是,好消息是在如今的浏览器环境中,支持度变得越来越好,让我们看看如何在当今的浏览器中使用ES6 Modules吧!
当前的 ES Modules 是怎么用的?#
Safari,Chrome,Firefox 和 Edge 均支持ES6 Modules导入语法,用起来会像是这样子:
1 | <script type="module"> |
1 | // html.js |
或者作为外部脚本来获取:
1 | <script type="module" src="app.js"></script> |
1 | // app.js |
只需要简单的把 type="module"
这一句添加到 script 标签里,浏览器将其识别为 ES Modules,并遍历所有的 import 路径,并且只下载和执行一次;
旧的浏览器不会执行未知类型的脚本,但可以使用 nomodule
属性添加一个后备脚本;
1 | <script type="module" src="module.js"></script> |
Requirements#
你需要一台服务器才能导入ES Modules 中 import 的内容,但它不能在file://
协议上使用。你可以使用 npx serve
在当前目录中启动服务器以进行本地测试。
如果希望在不同的域中加载ES Modules,则需要启用CORS;
如果你有足够的信心在现今的生产环境中尝试这样做,你仍然需要为旧的浏览器创建单独的包。browseres -es-module-loader
是一个polyfill,它符合规范。但是,仍然不推荐用于生产。
1 | <script nomodule src="https://unpkg.com/browser-es-module-loader/dist/babel-browser-build.js"></script> |
性能#
不要放弃你的构建工具,比如 Babel 和 Webpack,因为尽管浏览器在不断寻求实现优化资源获取的方法,在将来的ES Modules中仍然会存在性能和收益的缺陷。
为什么我们要将各个资源放在一起去获取#
今天,我们将 JavaScript 捆绑在一起以减少 HTTP请求 的数量,因为网络通常是加载web页面中最慢的部分。这在今天仍然是一个非常现实的问题,但是未来是光明的:ES Modules 配合 HTTP2 能够通过 服务器推送 和 浏览器预加载 来同时获取多个资源。
预加载#
link rel=”modulepreload” 会快就能使用,它不会让浏览器逐个地解析脚本,而是生成一个可控制地网络流;一下举例说明:
如果不添加这个属性:
1 | <script type="module" src="./app.js"></script> |
那么网络流是这样的:
1 | ---> GET index.html |
而当你通过这个属性明确地告诉浏览器,页面需要 html.js
和 lib.js
,就像这样:
1 | <link rel="modulepreload" href="html.js"> |
1 | ---> GET /index.html |
HTTP2#
与只能传递一个资源的HTTP1.1相比,HTTP2能够在单个响应中推送多个资源。 这将有助于使网络请求数量保持最少。
在我们的示例中,可以在一个请求中获取 index.html
、app.js
和 html.js
:
1 | ---> GET /index.html |
缓存#
传递多个较小的ES Modules可能有利于发挥缓存地优势,因为浏览器仅需要获取更新过的模块。如果使用大量脚本绑定在一个Module内,如果更新,就需要一次更新整个Modules;
async / defer#
ES modules 默认不会阻塞渲染进程,类似于 <script defer>
,如果你的 Modules 不需要按照HTML中定义的顺序执行,你可以添加 async
属性令其加载后尽快执行;
Libraries#
许多流行的库现在已开始以 ES Modules 的形式发布,但是它们仍以构建工具为目标,而不是本文所介绍的这种直接导入的方式。
这个不起眼的import会触发640个请求:
1 | <script type="module"> |
如果我们只导入我们需要的那一个函数呢?我们现在只有119个请求:
1 | <script type="module"> |
这是个小例子,用来例证目前为止 lodash-es
并不是直接用来给浏览器 import 的,要做到能够直接import,你仍然需要创建自己的 ES 库。
浏览器的支持#
如下表所示,浏览器对 ES Modules 的支持很好(并且一直在改进)。
现在是时候开始在浏览器中尝试ES Modules了。很快,你就可以在没有编译器或构建工具的情况下,在所有现代浏览器中使用ES Modules。