import.meta - JavaScript | MDN
import.meta
是一个宿主环境创建的可扩展的 null 原型对象,其所有属性均可写、可配置、可枚举。规范没有在对象上明确定义任何属性,但是宿主环境通常会定义以下属性:
- url
- resolve()
描述
import.meta
语法由关键字 import
、一个点符号和 meta
标识符组成。因为 import
是保留字而不是标识符,其并不是属性访问器而是特殊的表达式语法。
import.meta
元属性在 JavaScript 模块中可用;在模块之外(包括在模块中直接调用 eval())使用 import.meta
是语法错误。
import.meta.url
- 示例(传递查询参数):
在 import
声明中使用查询参数允许为特定模块传递参数,可作为应用程序内从 window.location(或在 Node.js 中从 process.env
)读取参数的补充方式。例如下面的 HTML:
<script type="module">
import "./index.mjs?someURLInfo=5";
</script>
index.mjs
模块可以通过 import.meta
获取 someURLInfo
参数:
// index.mjs
new URL(import.meta.url).searchParams.get("someURLInfo"); // 5
import.meta.resolve()
import.meta.resolve()
allows a script to access the module specifier resolution algorithm for a name, like this:
// Script at https://example.com/main.js
const helperPath = import.meta.resolve("./lib/helper.js");
console.log(helperPath); // "https://example.com/lib/helper.js"
Note that import.meta.resolve()
only performs resolution and does not attempt to load or import the resulting path. Therefore, its return value is the same regardless of whether the returned path corresponds to a file that exists, and regardless of whether that file contains valid code for a module. This allows import.meta.resolve()
to be a synchronous operation.
导入映射(import map)
关于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>
2. 在 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(imp);
</script>
这种方式需要确保创建和插入 import map
脚本标签之前进行(如上所述),因为修改一个已经存在的import map
对象不会有任何效果。
import maps
引入同一个包的不同版本
通过import maps
引入同一个包的不同版本是一件非常简单的事情,你只需要把不同版本的标识符在映射中写出来就行。就像下面这样。
<script type="importmap">
{
"imports": {
"vue@2/": "https://cdn.jsdelivr.net/npm/vue@2.0.0/dist/vue.esm-browser.js",
"vue@3/": "https://cdn.jsdelivr.net/npm/vue@3.0.0/dist/vue.esm-browser.js"
}
}
</script>
import maps
can i use
最后说一下关于import maps
的兼容性问题,首先上一张can i use
上的截图:
image.png
目前已经得到了多个主流浏览器的支持,但还没有被所有浏览器兼容。特别国内常用的QQ浏览器
、安卓手机常用的UC浏览器
等。
然而,对于一些旧版本的浏览器或者不支持导入映射的浏览器,可以通过使用 polyfill
或者额外的加载器库(如 SystemJS
)来实现导入映射的功能。
总之,虽然导入映射已经得到了多个主流浏览器的支持,但在开发过程中需要考虑到不同浏览器的兼容性问题。