JavaScript知识点Import Maps
关于import maps
不知道大家有没有听过,我之前只是知道JavaScript
增加的新特性,具体什么作用也没有进行学习研究过,周末学习中看到有人使用它,于是也尝试了解一下~,后来发现确实还是挺好用的,所以写一篇文章给大家分享一下。
我主要从以下几个方面来简单介绍一下import maps
:
import maps
是什么
import map
直译过来是 导入映射,它与模块的使用有关,一般我们在项目中导入模块,会调用require
方法,或者使用import
语句或方法,引入的模块通常使用 npm 之类的包管理器进行管理。但是import map
提供了一种支持,让我们可以直接在页面上管理模块,不需要通过打包构建。
import maps
已经成为了一个 Web 标准,并且在 2021 年 7 月正式通过了 W3C 的标准化流程;但是由于这个特性比较新,很多浏览器不支持,后面我们详细聊聊兼容情况。
接下来看一下import map
怎么工作的…
import maps
怎么使用
在import maps
中,可以使用一个 JavaScript
对象来定义模块标识符与对应 URL
的映射关系,例如:
<script type="importmap">
{
"imports": {
"lodash": "https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js",
"react": "/node_modules/react/index.js"
}
}
</script>
在上述示例中,定义了 lodash
模块的 URL
为 https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js,而 react
模块的 URL
则为相对路径 /node_modules/react/index.js
。
通过importmap
,可以在模块中使用字符串形式的模块名称来导入其他模块,而不必关心实际模块资源的 URL
,例如:
<script type="module">
import _ from "lodash";
import React from "react";
</script>
这样,JavaScript
引擎会自动根据导入映射中定义的映射关系来加载模块资源,并将其绑定到相应的模块变量上。
总之import maps
使用其实非常的简单,是通过在html
文档的script
标签中,使用json
对象来配置所有需要再当前 html 文档中需要引入的模板。
如果这个json
映射表内容比较多,我们还可以将它放在其他文件中,然后通过src
属性去链接它,例如:
<script scr="xxx_importmaps.json"></script>
上面一起简单的认识了import maps
相信不少人和我一样,都在想一个问题,这种实现方式有什么优势呢,或者说究竟能解决哪些问题, 带着疑问我们进入下一节
import maps
有何优势?
换句话说,我们需要先知道import maps
可以用来做什么?
动态加载模块
import maps
支持动态加载模块,可以在需要的时候才进行加载,避免了一次性加载所有模块的开销,提高了应用程序的性能和用户体验。
这里举例说明一下,例如在 Vue2
的项目中,文章页面支持播放视频,但是文章中没有视频时,我可以不用加载播放器,项目中动态加载 player.min.js
:
1.首先在index.html
文件中添加import maps
<head>
<script type="importmap">
{
"imports": {
"player": "https://example.com/path/to/player.min.js"
}
}
</script>
</head>
- 在 Vue 组件中动态加载
player.min.js
文件
可以在需要使用播放器的 Vue 组件中使用动态加载 player.min.js
文件的代码,例如:
export default {
data() {
return {
player: null,
};
},
methods: {
loadPlayer() {
if (this.player) {
return Promise.resolve(this.player);
}
return import("player").then((Player) => {
this.player = new Player.default();
return this.player;
});
},
play() {
this.loadPlayer().then((player) => {
player.play();
});
},
},
};
减少网络请求
使用 import maps
可以减少网络请求的主要原因是,可以将多个模块合并成一个请求,从而减少了网络请求的次数,提高了页面的加载速度。
如果一个页面需要引入多个模块,如果不使用import maps
,每个模块都需要通过一个独立的请求进行加载。而使用 import maps
,可以将多个模块合并成一个请求,从而减少了网络请求的次数。例如:
<script type="importmap">
{
'lodash': 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js',
'vue': 'https://cdn.jsdelivr.net/npm/vue@3.0.0/dist/vue.esm-browser.js',
'axios': 'https://cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js'
};
</script>
// 定义一个映射表
// 加载模块
Promise.all([import("lodash"), import("vue"), import("axios")]).then(
([_, { createApp }, axios]) => {
const app = createApp({
/* ... */
});
axios.get(/* ... */);
}
);
在上面的代码中,在加载模块时,我们使用了 Promise.all
方法将多个模块的导入操作合并到一个 Promise
对象中,这样浏览器就可以将多个模块的请求合并成一个请求,从而减少了网络请求的次数。需要注意的是,在使用 import maps
减少网络请求时,我们需要避免将过多的模块合并到一个请求中,否则可能会导致请求过大,影响页面的加载速度。通常建议将多个模块合并到一个请求中时,选择具有相似功能或关联度高的模块进行合并,同时需要进行适当的测试和优化,以确保页面的加载速度和性能。
模块依赖关系管理
通过在import maps
中定义模块之间的依赖关系,可以更加方便地进行依赖管理,并且可以在不同的环境中(例如开发环境和生产环境)使用不同的导入映射,从而更好地控制依赖关系。假设我们有一个页面需要加载三个模块:module1.js
、module2.js
和 module3.js
。其中,module2.js
和 module3.js
依赖于 module1.js
,也就是说,需要先加载 module1.js
,再加载 module2.js
和 module3.js
。在不使用 Import Maps 的情况下,我们需要手动管理这些模块的加载顺序,代码如下:
// 加载模块 1
const script1 = document.createElement("script");
script1.src = "module1.js";
script1.onload = () => {
// 加载模块 2
const script2 = document.createElement("script");
script2.src = "module2.js";
script2.onload = () => {
// 加载模块 3
const script3 = document.createElement("script");
script3.src = "module3.js";
document.head.appendChild(script3);
};
document.head.appendChild(script2);
};
document.head.appendChild(script1);
上面的代码中,我们通过创建 script
标签来加载模块,并使用 onload
事件来控制模块的加载顺序,保证模块的依赖关系正确。这种方式需要手动管理模块的加载顺序,代码比较冗长,且容易出错。
使用 Import Maps 可以简化这个过程,代码如下:
<script type="importmap">
{
'module1': 'module1.js',
'module2': { deps: ['module1'], url: 'module2.js' },
'module3': { deps: ['module1'], url: 'module3.js' },
};
</script>
// 加载模块
Promise.all([
import(moduleMap["module1"].url),
import(moduleMap["module2"].url),
import(moduleMap["module3"].url),
]).then(([module1, module2, module3]) => {
// ...
});
其中每个模块都定义了其依赖关系和对应的 URL。然后在加载模块时,我们只需要按照模块依赖关系的顺序,依次导入每个模块即可,不需要手动管理模块的加载顺序。这种方式代码比较简洁,且可以更好地管理模块的依赖关系。
其他用法
动态构建 import map
import maps
可以根据条件任意的动态配置,这个能力就相当于支持了动态加载模块,可以在需要的时候才进行加载,避免一次性加载所有的模块,提高应用程序的性能和用户体验。这里举例说明一下,例如我的文章页面支持播放视频,但是文章中没有视频,我可以不用加载播放器:
<script>
const importMap = {
imports: {
lazyload: "player.min.js",
},
};
const imp = document.createElement("script");
imp.type = "importmap";
imp.textContent = JSON.stringify(importMap);
document.currentScript.after(im);
</script>
这种方式需要确保创建和插入 import map
脚本标签之前进行(如上所述),因为修改一个已经存在的import map
对象不会有任何效果。
import maps
引入同一个包的不同版本
通过import maps
引入同一个包的不同版本是一件非常简单的事情,你只需要把不同版本的标识符在映射中写出来就行。就像下面这样。
<script>
const importMap = {
imports: {
lazyload: "player.min.js",
},
};
const imp = document.createElement("script");
imp.type = "importmap";
imp.textContent = JSON.stringify(importMap);
document.currentScript.after(im);
</script>
import maps
can i use
最后说一下关于import maps
的兼容性问题,首先上一张can i use
上的截图:
目前已经得到了多个主流浏览器的支持,但还没有被所有浏览器兼容。特别国内常用的QQ浏览器
、安卓手机常用的UC浏览器
等。
然而,对于一些旧版本的浏览器或者不支持导入映射的浏览器,可以通过使用 polyfill
或者额外的加载器库(如 SystemJS
)来实现导入映射的功能。
总之,虽然导入映射已经得到了多个主流浏览器的支持,但在开发过程中需要考虑到不同浏览器的兼容性问题。