切换导航
简历
工具
演示
查询
导航菜单
服务端
30
前端
114
数据库
82
进阶开发
8
桌面程序
2
QA
15
软件使用
36
规范协议
5
操作系统
11
AI
0
复用资源
6
硬件
0
标签
HTML
正则表达式
Json
包管理工具
vite
CSS
XPath
JavaScript
Jquery
less
ES6
Vue
React
vue 3
Layui
editor.md
sea.js
Zepto.js
mysql.js
mssql.js
NodeJs
Express
前端template
YAML
杂项
外部链接
.NET Core SDK和运行时安装包
淘宝 NPM 镜像
查询网
Tables Generator线上表格转换工具
引用cdn在线脚本和样式
首页
前端
Javascript ECMAScript 模块化规范的进阶特性
[TOC] <h1>ECMAScript 模块化规范的进阶特性</h1> 下面详细介绍 **Import Maps** 和 **`import.meta`** 这两个 ESM 进阶特性。 ## 1. Import Maps(导入映射) |**Import Maps(导入映射)是什么**| |--| |Import Maps 是浏览器原生支持的一种机制,允许开发者在 HTML 中通过 `<script type="importmap">` 声明一个 JSON 对象,来**控制模块导入说明符(specifier)如何解析为实际 URL**。| |简单说:它可以让你在 `import` 语句中写类似 `import lodash from 'lodash'` 这样的“裸模块名”,而不需要知道具体的 CDN 路径或本地路径。| |**诞生的背景:在没有 Import Maps 之前**| |--| |在浏览器中使用 ESM 时,`import` 语句的说明符必须是**相对路径**(`'./utils.js'`)、**绝对路径**(`'/lib/math.js'`)或**完整 URL**(`'https://cdn.com/lodash.js'`)。| |这导致一个问题:无法直接使用 npm 包名(如 `'lodash'`)导入,因为浏览器不知道去哪里找这个包。| |通常需要借助打包工具(Webpack、Vite)或者手动改写导入语句。| |**解决的问题**| |--| |Import Maps 让浏览器原生支持了“裸模块导入”,同时也支持多环境切换、版本管理、依赖重定向等高级功能。| > **注意:**根据 HTML Import Maps 规范,映射的 value 必须按 "URL-like module specifier" 解析,只有以下形式才算合法: + 以 / 开头(根相对路径) + 以 ./ 或 ../ 开头(相对路径) + 绝对 URL(如 https://...、data:...) ### 1.1. 使用方式 **基本语法** 在 HTML 中插入一个 `<script type="importmap">` 标签,里面是一个特定结构的 JSON。 ```html <script type="importmap"> { "imports": { "lodash": "https://cdn.skypack.dev/lodash@4.17.21", "lodash/": "https://cdn.skypack.dev/lodash@4.17.21/", "my-utils": "/static/js/utils.js" }, "scopes": { "/some-subdir/": { "lodash": "https://cdn.skypack.dev/lodash@4.17.21?dev" } } } </script> ``` 然后就可以在模块中直接使用这些映射后的名称: ```javascript // main.js import _ from 'lodash'; // 实际加载 https://cdn.skypack.dev/lodash@4.17.21 import { cloneDeep } from 'lodash/cloneDeep'; // 映射到 https://cdn.skypack.dev/lodash@4.17.21/cloneDeep import { helper } from 'my-utils'; // 映射到 /static/js/utils.js ``` |**结构说明**| |--| |`imports`:核心映射,将模块名称映射到 URL。支持精确映射(`'lodash'`)和前缀映射(`'lodash/'`,末尾斜杠匹配所有以该前缀开头的说明符)| |`scopes`(可选):针对特定路径作用域的映射。当模块在某个特定作用域(比如 `/some-subdir/` 内的模块)下被导入时,使用作用域内的映射覆盖顶层的 `imports`。主要用于不同模块可能需要不同版本(例如测试环境)。| |**注意事项**| |--| |必须是有效的 JSON:不能有注释或末尾逗号。| |位置:`<script type="importmap">` 必须**在所有使用导入的模块脚本之前**声明。| |只能有一个:目前每个页面只支持单个 import map,多个 import map 会被合并(非递归合并,后面的覆盖前面的顶级键)。| |安全性:URL 必须支持 CORS。| ### 1.2. 实际示例 假设项目依赖 `lodash` 和 `moment`,希望统一管理版本,并且开发和生产环境使用不同 CDN(通过动态生成 import map)。 ```html <!-- index.html --> <!DOCTYPE html> <html> <head> <script type="importmap"> { "imports": { "lodash": "https://cdn.skypack.dev/lodash@4.17.21", "moment": "https://cdn.skypack.dev/moment@2.29.4" } } </script> <script type="module" src="./app.js"></script> </head> <body></body> </html> ``` ```javascript // app.js import _ from 'lodash'; import moment from 'moment'; console.log(_.VERSION); // 4.17.21 console.log(moment.version); // 2.29.4 ``` **多环境切换**(例如生产环境使用不同的 CDN 或本地静态服务):可以在服务端渲染 HTML 时动态注入不同的 import map。 ### 1.3. 作用与优势 - **简化导入路径**:不需要写又长又容易变的 CDN 地址。 - **集中管理依赖**:所有依赖的 URL 都在一个地方维护。 - **支持裸模块**:让浏览器 ESM 可以像 Node.js 和打包工具一样使用 npm 包名。 - **版本升级方便**:只需修改 import map,而不用改动每个源码文件。 - **依赖替换**:可以轻松地将一个模块映射到另一个实现(polyfill 或 mock)。 ### 1.4. 限制 - **浏览器兼容性**:目前现代浏览器(Chrome 89+, Edge 89+, Firefox 108+, Safari 16.4+)支持。旧浏览器需要 polyfill(如 `es-module-shims`)。 - **无法覆盖已经加载的模块**:import map 必须提前定义。 - **每个页面只能有一个顶级 import map**,但在 iframe 中可以有独立的 map。 ## 2. import.meta(模块元数据) |**import.meta(模块元数据)是什么?**| |--| |import.meta 是一个**宿主环境提供的对象**,用于向 JavaScript 模块暴露特定于当前模块的元数据。它是 ESM 中可访问的、包含模块上下文信息的属性。| |主要属性|说明| |--|--| ||不同环境(浏览器、Node.js、Deno 等)会在 `import.meta` 上挂载不同的属性和方法。最常用且跨平台支持较好的有| |`import.meta.url`|返回当前模块的完整 URL(字符串)。例如,在浏览器中,它会是 `https://example.com/js/main.js`;在 Node.js 中,它会是 `file:///path/to/module.js`| |`import.meta.resolve?`(实验性)|1️⃣**浏览器**:`import.meta.resolve(specifier)` 可以根据 import map 解析模块说明符,返回最终的 URL。这是较新的提案,部分浏览器支持。<br>2️⃣**Node.js**:`import.meta.resolve` 从 Node.js 18.19+ / 20.0+ 开始支持(实验性)。| ||**Node.js 特定元数据**| |`import.meta.filename`|当前模块的绝对路径(类似于 `__filename`,但 ESM 中无 `__filename`)| |`import.meta.dirname`|当前模块所在目录的绝对路径(类似于 `__dirname`)| ||注意:Node.js 中这些属性需要通过 `--experimental-import-meta-resolve` 启用,或者使用 `new URL(‘.’, import.meta.url)` 自行构建。| ||**Deno 特定元数据**| |`import.meta.main`|布尔值,表示当前模块是否为入口模块(类似于 `require.main === module`)| ### 2.1. 使用方式 **示例 1:获取当前模块的 URL(浏览器/Node.js 通用)** ```javascript // anywhere.js console.log(import.meta.url); // 浏览器:https://example.com/path/to/anywhere.js // Node.js:file:///home/user/project/anywhere.js ``` **示例 2:基于当前模块路径构造资源 URL(加载同目录下资源)** 这在浏览器中非常有用:你想动态加载图片、JSON 文件等,且路径相对于当前模块。 ```javascript // 📁 components/button.js const imgURL = new URL('./icon.png', import.meta.url).href; const img = document.createElement('img'); img.src = imgURL; // 实际请求的 URL 会基于当前模块的 URL 解析出 './icon.png' ``` ```javascript // 加载同目录的 JSON 文件 const response = await fetch(new URL('./data.json', import.meta.url)); const data = await response.json(); ``` **解释**:`new URL(relativePath, import.meta.url)` 可安全地构造绝对 URL,不受页面基础路径影响。 **示例 3:在 Node.js 中模拟 `__dirname`** Node.js 的 ESM 模块中没有 `__dirname` 和 `__filename`,但可以通过 `import.meta.url` 构建: ```javascript // utils.js (ESM) import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); console.log(`Current directory: ${__dirname}`); ``` **示例 4:动态解析模块路径(浏览器 + import map)** ```javascript // 假设有 import map 映射 'lodash' 到具体 URL const lodashURL = import.meta.resolve('lodash'); console.log(lodashURL); // 输出解析后的完整 URL // 可以在运行时动态加载 const _ = await import(lodashURL); ``` 注意:`import.meta.resolve` 仍需要浏览器支持(Chrome 105+ 开启实验性标志,或者使用 polyfill)。 **示例 5:判断入口模块(类似 `require.main === module`)** Node.js 中可以通过检查 `import.meta.url` 是否等同于 `process.argv[1]` 构建的 URL,但更规范的是引入 `import.meta.main`?实际上 Node.js 官方并未直接提供 `import.meta.main`,不过可以这样实现: ```javascript // main.js import { fileURLToPath } from 'node:url'; import { pathToFileURL } from 'node:url'; const isMain = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]; if (isMain) { console.log('This module is the entry point'); } ``` Deno 中直接使用 `import.meta.main` 即可。 ### 2.2. 作用与优势 - **环境无关的资源定位**:`import.meta.url` 让模块能够可靠地加载自己附近的资源(图片、配置文件、子模块),而不依赖全局 `document.baseURI` 或 `location.href`。 - **调试信息**:模块可以知道自己的来源,方便打印日志或上报错误位置。 - **支持动态代码生成**:在某些框架中,可以利用 `import.meta.url` 做热更新或动态导入同级模块。 - **填补 ESM 与 CJS 的 API 鸿沟**:例如在 Node.js 的 ESM 中替代 `__dirname` / `__filename`。 ### 2.3. 与其他环境的关系 | 环境 | `import.meta.url` | 其他元数据 | | -------------- | ----------------- | -------------------------------------------- | | 浏览器 | ✅ | `import.meta.resolve`(实验性) | | Node.js | ✅ | 需通过 `url` 和 `path` 模块模拟 `__dirname` | | Deno | ✅ | `import.meta.main`, `import.meta.resolve` | | Web Workers | ✅ | 同浏览器 | --- ## 三、总结 - **Import Maps**:**解决浏览器中模块导入说明符的映射问题**,支持裸模块、集中管理依赖、版本重定向。使用 `<script type="importmap">` 声明 JSON 映射。 - **import.meta**:**提供当前模块的元数据**,核心是 `import.meta.url`,用于获取模块 URL,从而可以动态加载相对路径资源;此外还有实验性的 `import.meta.resolve` 用于解析模块说明符。
扫一扫手机看
点击排行
windows 文件资源管理器使用
Layui 弹窗open使用说明
SQL Server 中字符串函数使用
C# 使用QQ邮箱代理做邮件接收功能
SQL Server关于apply的两种形式cross apply 和 outer apply
ASP.NET MVC 模型的自定义验证
nvm安装及常用命令
SQL Server 中数据类型转换
SQL Server 中WITH AS短语的使用
IE浏览器下AJAX缓存问题导致数据不更新的解决办法
订阅号