大家好,我是Echa。
好消息, 2023年4月1号 愚人节这天 JavaScript import maps (导入映射)现在支持跨浏览器了,下面小编详细介绍:
如何不基于构建工具优雅的实现模块导入?
JavaScript import maps 现在支持跨浏览器
当 ES Module 最开始作为一种新的 JavaScript 模块化方案在 ES6 中被引入的候,其实是通过在 import 语句中强制指定相对路径或绝对路径来实现的。
复制
import dayjs from "https://cdn.skypack.dev/dayjs@1.10.7"; // ES modules console.log(dayjs("2023-04-1").format("YYYY-MM-DD"));
1.
2.
这和其他常见的模块化系统(例如 CommonJS)的工作方式略有不同,并且在使用像 webpack 这样的模块打包工具的时候会使用更简单的语法:
复制
const dayjs = require('dayjs') // CommonJS import dayjs from 'dayjs'; // webpack
1.
2.
3.
在这些系统里,模块导入语句通过 Node.js 运行时或相关构建工具映射到特定(版本)的文件。用户只需要在 import 语句中直接编写模块说明符(通常是包名),模块就可以自动处理。
由于开发人员已经熟悉了这种从 npm 导入包的方式,因此必须要先经过一个的构建步骤才能确保以这种方式编写的代码可以在浏览器中运行。
Import maps 就可以解决这个问题,它可以将模块说明符(包名)自动映射到它的相对或绝对路径。从而让我们不使用构建工具也能使用简洁的模块导入语法。
我们可以通过 HTML 中的 <script type="importmap"> 标签来指定一个 Import maps。
复制
<script type="importmap">{ "imports": {"dayjs": "https://cdn.skypack.dev/dayjs@1.10.7", }}</script>
1.
2.
3.
4.
5.
6.
7.
为了成功的在模块解析之前对其进行解析。这个 script 标签必须放在文档中第一个 <script type="module"> 标签之前(最好放在 <head>中),另外,目前每个 HTML 只允许编写一个 Import maps 。
复制
<script type="module"> import dayjs from 'dayjs'; console.log(dayjs("2023-04-01").format("YYYY-MM-DD"));</script>
1.
2.
3.
4.
5.
在 script 标签内,我们可以通过一个 JSON 对象来为文档中的脚本所需导入的模块指定所有必要的映射。一个典型的 importmap 结构如下所示:
复制
<script type="importmap">{ "imports": {"react": "https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js","react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js" "square": "./modules/square.js","lodash": "/node_modules/lodash-es/lodash.js" }}</script>
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
在上面的 import 对象中,每个属性对应一个映射。映射的左侧是导入说明符的名称(一般是包名),而右侧是说明符需要映射到的相对或绝对路径。在映射中指定相对路径时,必须要确保它们始终以 /、../或 ./ 开头。
另外,importmap 中声明的包并不一定意味着它一定会被浏览器加载。页面上的脚本没有使用到的任何模块都不会被浏览器加载,即便你在 importmap 中声明了它。
编写好 importmap 之后,你就可以在后面的脚本中直接使用 ES Module 语法了。
复制
<script type="module"> import { cloneDeep } from 'lodash'; const objects = [{ a: 1 }, { b: 2 }]; const deep = cloneDeep(objects); console.log(deep[0] === objects[0]);</script>
1.
2.
3.
4.
5.
6.
7.
8.
你还可以在外部文件中指定你的映射,然后使用 script 的 src 属性链接到这个文件(Content-Type Header 必须要设置为 application/importmap+json 才能正常加载)。
复制
<script type="importmap" src="importmap.json"></script>
1.
不过尽量不要使用这种方式,因为它的性能比直接内联编写要差。
除了将一个说明符映射到模块之外,你还可以将一个说明符映射到包含多个模块的包:
复制
<script type="importmap">{ "imports": {"lodash/": "/node_modules/lodash-es/" }}</script>
1.
2.
3.
4.
5.
6.
7.
这种编写方式可以让你直接导入指定路径中的任何模块,相应的,浏览器也会把所有组件模块下载下来。
复制
<script type="module"> import toUpper from 'lodash/toUpper.js'; import toLower from 'lodash/toLower.js'; console.log(toUpper('ConardLi')); console.log(toLower('ConardLi'));</script>
1.
2.
3.
4.
5.
6.
7.
你也可以基于一些条件在 script 中添加一个动态映射,比如,在下面的示例中我们通过判断是否存在 IntersectionObserver API 来导入不同文件:
复制
<script> const importMap = { imports: { lazyload: 'IntersectionObserver' in window ? './lazyload.js' : './lazyload-fallback.js', }, }; const im = document.createElement('script'); im.type = 'importmap'; im.textContent = JSON.stringify(importMap); document.currentScript.after(im); </script>
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
使用 importmap 我们可以将不同的版本的模块映射到不同的包名中:
复制
<script type="importmap"> {"imports": { "lodash@3/": "https://unpkg.com/lodash-es@3.10.1/", "lodash@4/": "https://unpkg.com/lodash-es@4.17.21/"} }</script>
1.
2.
3.
4.
5.
6.
7.
8.
另外你还可以通过 scopes 来实现同一个包不同模块的更细粒度的版本控制:
复制
<script type="importmap"> {"imports": { "lodash/": "https://unpkg.com/lodash-es@4.17.21/"},"scopes": { "/static/js": {"lodash/": "https://unpkg.com/lodash-es@3.10.1/" }} }</script>
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
/static/js 下的模块会使用 3.10.1 版本,而其他模块会使用 4.17.21 版本。
这项技术目前在 Chrome 和 Edge 浏览器 89 及更高版本提供了全面支持,但 Firefox、Safari 和一些移动浏览器还没有支持。我们可以通过下面的代码来判断浏览器的支持情况:
复制
if (HTMLScriptElement.supports && HTMLScriptElement.supports('importmap')) { // import maps is supported}
1.
2.
3.
对于没有提供支持的浏览器,我们可以使用下面
这个 polyfill:https://github.com/guybedford/es-module-shims
另外官方也推荐了一些其他 importmap 相关的 polyfill 和工具:
随着 Safari 16.4 的发布,WebKit 引擎也支持了 Import Mpas。
ES模块是在web应用程序中包含和重用JavaScript代码的一种现代方式。它们受到现代浏览器的支持,并提供了一些优于旧的非模块化JavaScript开发方法的优势。
使用ES模块的一种现代方式是使用<script type=“importmap”>标记。此标记允许您定义外部模块名称到其相应URL的映射,这使得在代码中包含和使用外部模块变得更容易。
要使用<script type=“importmap”>方法,首先需要将其添加到HTML文档的<head>部分。在标记中,您可以定义一个JSON对象,该对象将模块名称映射到相应的URL。例如:
复制
<script type="importmap"> {"imports": { "browser-fs-access": "https://unpkg.com/browser-fs-access@0.33.0/dist/index.modern.js"} }</script>
1.
2.
3.
4.
5.
6.
7.
此代码定义了一个名为“browser fs access”的外部模块,并将其映射到unpkg CDN上托管的浏览器fs访问库的URL。有了这个映射,现在可以使用import关键字在代码中包含浏览器fs访问库。请注意,import关键字仅在具有type=“module”属性的脚本标记中可用。
复制
<button>Select a text file</button><script type="module"> import {fileOpen} from 'browser-fs-access'; const button = document.querySelector('button'); button.addEventListener('click', async () => {const file = await fileOpen({ mimeTypes: ['text/plain'],});console.log(await file.text()); });</script>
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
与较旧的非模块化JavaScript开发方法相比,使用<script type=“importmap”>标记和import关键字提供了一些好处。它允许您清晰明确地指定代码所依赖的外部模块,这使得代码更容易理解和维护。总的来说,使用带有<script type=“importmap”>标签的ES模块是在web应用程序中包含和重用JavaScript代码的一种现代而强大的方式。您可以按如下方式提供功能检测支持:
复制
if (HTMLScriptElement.supports('importmap')) { // The importmap feature is supported.}
1.
2.
3.
一台电脑,一个键盘,尽情挥洒智慧的人生;几行数字,几个字母,认真编写生活的美好;
一 个灵感,一段程序,推动科技进步,促进社会发展。