本文将向您展示如何在浏览器中使用 ES 模块。

直到最近,JavaScript 还没有模块的概念。 无法直接引用一个 JavaScript 文件或将其包含在另一个 JavaScript 文件中。 随着应用程序的规模和复杂性的增长,这使得为浏览器编写 JavaScript 变得棘手。

一种常见的解决方案是使用<script>标记在网页中加载任意脚本。 但是,这带来了自己的问题。 例如,每个脚本都会启动一个阻止渲染的 HTTP 请求,这可能会使重载 JS 的页面显得迟钝且缓慢。 依赖管理也变得复杂,因为加载顺序很重要。

ES6(ES2015)通过引入单个本机模块标准来解决这种情况。 (您可以在此处阅读有关 ES6 模块的更多信息 。)但是,由于浏览器最初对 ES6 模块的支持不佳,人们开始使用模块加载器将依赖项捆绑到单个 ES5 跨浏览器兼容文件中。 此过程介绍了自己的问题和复杂程度。

但是,好消息即将到来。 浏览器支持变得越来越好,因此让我们看一下如何在当今的浏览器中使用 ES6 模块。

当前的 ES 模块格局

Safari,Chrome,Firefox 和 Edge 均支持 ES6 模块导入语法。 这是他们的样子。

1
2
3
4
5
6
<script type="module">
import { tag } from './html.js'

const h1 = tag('h1', '👋 Hello Modules!')
document.body.appendChild(h1)
</script>
1
2
3
4
5
6
7
// html.js
export function tag (tag, text) {
const el = document.createElement(tag)
el.textContent = text

return el
}

或作为外部脚本:

1
<script type="module" src="app.js"></script>
1
2
3
4
5
// app.js
import { tag } from './html.js'

const h1 = tag('h1', '👋 Hello Modules!')
document.body.appendChild(h1)

只需将type="module"添加到脚本标签中,浏览器就会将它们加载为 ES 模块。 浏览器将遵循所有导入路径,仅下载和执行每个模块一次。

1

较旧的浏览器不会执行具有未知 “类型” 的脚本,但是您可以使用nomodule属性定义后备脚本:

1
2
<script type="module" src="module.js"></script>
<script nomodule src="fallback.js"></script>

需求

您将需要一台服务器,以便能够通过导入进行获取,因为它不适用于file://协议。 您可以使用npx serve在当前目录中启动服务器以进行本地测试。

如果要在其他域上加载 ES 模块,则需要启用 CORS。

如果您足够大胆地尝试在今天的产品中进行尝试,则仍然需要为较旧的浏览器创建单独的捆绑软件。 在遵循规范的浏览器 - es-module-loader 中有一个 polyfill。 但是,根本不建议将其用于生产。

1
2
3
<script nomodule src="https://unpkg.com/browser-es-module-loader/dist/babel-browser-build.js"></script>
<script nomodule src="https://unpkg.com/browser-es-module-loader"></script>
<script type="module" src="./app.js"></script>

性能

暂时不要扔掉 Babel 和 Webpack 等构建工具,因为浏览器仍在实现优化获取的方法。 尽管如此,将来使用 ES 模块仍会_有性能_缺陷收获

为什么我们是捆绑的

今天,我们捆绑了 JavaScript 以减少发出的 HTTP 请求的数量,因为_网络_通常是加载网页中最慢的部分。 今天,这仍然是一个非常有效的担忧,但未来是光明的:具有 HTTP2 的 ES 模块能够通过服务器推送和实现预加载的浏览器流式传输多个资产。

预装

link rel =“modulepreload” 即将发送到您附近的浏览器。 而不是让浏览器一一解析所有模块的导入,而是产生这样的网络瀑布……

1
<script type="module" src="./app.js"></script>
1
2
3
4
5
6
7
8
---> GET index.html
<---
---> GET app.js
<---
---> GET html.js
<---
---> GET lib.js
<---

… 您将能够预先告诉浏览器页面需要html.jslib.js ,从而使瀑布受到控制:

1
2
3
<link rel="modulepreload" href="html.js">
<link rel="modulepreload" href="lib.js">
<script type="module" src="./app.js"></script>
1
2
3
4
5
6
7
8
---> GET /index.html
<---
---> GET app.js
---> GET html.js
---> GET lib.js
<---
<---
<---

HTTP2 与服务器推送

与只能传递一个资源的 HTTP1.1 相比,HTTP2 能够在单个响应中推送多个资源。 这将有助于使通过网络的往返次数保持最少。

在我们的示例中,有可能在单个请求中传递index.htmlapp.jshtml.js

1
2
3
4
5
---> GET /index.html
<--- index.html
<--- app.js
<--- html.js
<--- lib.js

快取

交付多个较小的 ES 模块可能有益于缓存,因为浏览器仅需要获取已更改的模块。 产生大捆的问题在于,如果更改一行,则会使整个捆无效。

异步 / 延迟

默认情况下,ES 模块不是渲染阻止,例如<script defer> 。 如果您的模块不需要按照在 HTML 中定义的顺序执行,则还可以添加async以在下载后立即执行它们。

函数库

流行的库现在已开始以 ES 模块的形式发布,但是它们仍以捆绑器为目标,而不是直接导入。

这个小小的导入会触发 _640 个请求_的瀑布:

1
2
3
<script type="module">
import \_ from 'https://unpkg.com/lodash-es'
</script>

如果我们做正确的事情 ,只是导入我们需要的一个函数,该怎么办? 我们仅有 119 个请求

1
2
3
<script type="module">
import cloneDeep from 'https://unpkg.com/lodash-es/cloneDeep'
</script>

这只是一个示例,说明lodash-es尚未构建为直接在浏览器中加载。 为此,您仍然需要使用 ES 模块作为目标来创建自己的捆绑软件。

浏览器支持

如下表所示,浏览器对 ES 模块的支持很好(并且一直在不断改进)。

点击查看框架的源代码

我可以使用 es6-module 吗? 来自 caniuse.com 的主要浏览器对 es6-module 功能的支持数据。

现在是开始在浏览器中尝试 ES 模块的时候了。 很快,您将可以在没有编译器或捆绑器的情况下在所有现代浏览器中使用它们。

转载自:在浏览器中使用 ES 模块(blog.csdn.net)
原文链接: https://www.sitepoint.com/using-es-modules/