JavaScript 之父 Brendan Eich 在最初设计这门语言时,并没有包含模块的概念。基于越来越多的工程需求,为了使用模块化进行开发,JavaScript 社区涌现出了多种模块标准,其中也包括 CommonJS 。一直到 2015 年 6 月,由 TC39 标准委员会正式发布 ES6(ECMAScript6.0),自此 JavaScript 语言才具备了模块这一特性。
模块
使用:
// calculator.js
export default {
name: 'calculator',
add: function(a, b){
return a + b;
}
}
// index.js
import calculator from './calculator.js':
const sum = calculator.add(2, 3);
console.log(sum); // 5
ES6 module 也是将每个文件作为一个模块,每个模块拥有自身的作用域,不同的是导入、导出语句。ES6版本将 import
和 export
作为保留关键字加入了进来(CommonJS 中的 module 并不属于关键字)。ES6 Module 会自动采用严格模式,该模式在ES5(ECMAScript 5.o)中只是一个可选项。也就是说,以前我们可以通过选择是否在文件开始时加上 use strict
来控制严格模式,而在ES6 Module 中不管开头是否有 use strict
都会采用严格模式。所以,在将原本是 CommonJS 的模块或任何未开启严格模式的代码改写为 ES6 Modulel 时要注意这一点。
导出
在 ES6 Module 中使用 export
命令来导出模块。export
有两种形式:
命名导出
// 写法1
export const name = 'calculator';
export const add = function(a, b) {return a + b; };
// 写法2
const name = 'calculator';
const add = function(a, b) {return a + b};
export { name, add }
在使用命名导出时,可以通过 as
关键字对变量重命名。如:
export { name, add as getSum }; // 在导入时即为 name 和 getSum
默认导出
我们可以将 export default
理解为对外输出了一个名为 default
的变量,因此不需要像命名导出一样进行变量声明,直接导出值即可。
// 导出字符串
export default 'This is calculator.js';
// 导出class
export default class {...}
// 导出匿名函数
export default function() {...}
混合导出
命名导出和默认导出可以一起使用,但每个模块默认导出只有一个,命名导出可以多个。
导入
ES6 Module 中使用 import 语法导入模块。
导入命名导出的模块
// calculator.js
const name ='calculator';
const add function(a, b) { return a + b; };
export { name, add };
// index.j
import { name, add } from './calculator.js';
add(2, 3);
导入变量名要与导出的变量名一致,且导入的变量都是只读的。
重命名导入:
import { name, add as calculateSum } from './calculator.js'
calculateSum(2, 3);
整体导入:
import * as calculator from'./calculator.js';
console.log(calculator.add(2, 3));
console.log(calculator.name);
导入默认导出的模块
// calculator.js
export default {
name: 'calculator',
add: function(a, b) { return a + b; }
}
// index.js
import myCalculator from '/calculator.js';
calculator.add(2, 3);
对于默认导出来说,import
后面直接跟变量名,并且这个名字可以自由指定(比如这里是 myCalculator),它指代了 calculator.js 中默认导出的值。从原理上可以这样去理解:
import { default as myCalculator } from './calculator.js';
混合导入
// index.js
import React, { Component } from 'react';
这里的 React
对应的是该模块的默认导出,而 Component
则是其命名导出中的一个变量。
注意,这里的 React
必须写在大括号前面,不能颠倒顺序,否则会提示语法错误。
复合写法
在工程中,有时需要把某一个模块导入之后立即导出,比如专门用来集合所有页面或组件的入口文件。此时可以采用复合写法:
export { name, add } from './calculator.js';
复合写法目前只支持被导入模块(这里的 calculator.js )通过命名导出的方式暴露出来的变量,默认导出则没有对应的复合形式,只能将导入和导出拆开写:
import calculator from "./calculator.js";
export default calculator;
import 说明符
import
语句的说明符是 from
关键字之后的字符串,例如 import { sep } from 'node:path'
中的 'node:path'
。说明符也用于 export from
语句,并作为 import()
表达式的参数。
有三种类型的说明符:
- 相对说明符,如
'./startup.js'
或'../config.mjs'
。它们指的是相对于导入文件位置的路径。这些文件扩展名始终是必需的。 - 纯说明符,如
'some-package'
或'some-package/shuffle'
。它们可以通过包名称来引用包的主要入口点,或者根据示例分别以包名称为前缀的包中的特定功能模块。只有没有"exports"
字段的包才需要包含文件扩展名。 - 绝对说明符,如
'file:///opt/nodejs/config.js'
。它们直接且明确地引用完整的路径。
裸说明符解析由Node.js 模块解析和加载算法处理。所有其他说明符解析始终仅使用标准的相对 URL 解析语义进行解析。
就像在 CommonJS 中一样,包中的模块文件可以通过在包名称后附加路径来访问,除非包的 package.json
包含 "exports"
字段,在这种情况下,包中的文件只能通过 "exports"
中定义的路径访问。
有关适用于 Node.js 模块解析中的裸说明符的这些包解析规则的详细信息,请参阅包文档。
强制文件扩展名
当使用 import
关键字解析相对或绝对的说明符时,必须提供文件扩展名。还必须完全指定目录索引(例如 './startup/index.js'
)。
此行为与 import
在浏览器环境中的行为方式相匹配,假设服务器是典型配置的。
export
export
声明用于从 JavaScript 模块中导出值。导出的值可通过 import
声明或动态导入来将其导入其他程序。导入绑定的值会在导出该绑定的模块中发生变化——当模块更新其导出绑定的值时,更新将在其导入值中可见。
要在源文件中使用 export
声明,运行时必须将该文件解释为模块。在 HTML 中,可通过在 <script>
标记中添加 type="module"
或由其他模块导入来实现。模块会自动以严格模式解释。
// 导出声明
export let name1, name2/*, … */; // also var import {name1, name2} from "...";
export const name1 = 1, name2 = 2/*, … */; // also var, let import {name1, name2} from "...";
export function functionName() { /* … */ } // import { functionName } from "...";
export class ClassName { /* … */ } // import { ClassName } from "...";
export function* generatorFunctionName() { /* … */ } // import { generatorFunctionName } from "...";
export const { name1, name2: bar } = o; // import { name1, bar } from "...";
export const [ name1, name2 ] = array; // import { name1, name2 } from "...";
// 导出列表
export { name1, /* …, */ nameN }; // import { name1, nameN } from "...";
export { variable1 as name1, variable2 as name2, /* …, */ nameN }; // import { name1, name2, nameN} from "...";
export { variable1 as "string name" }; // import { "string name" as variable1 } from "..."
export { name1 as default /*, … */ }; // import { default as name1 } from "..."; 或 import name1 from "...";
// 默认导出
export default expression; // import { default as name } from "..."; 或 import name from "...";
export default function functionName() { /* … */ } // import { default as name } from "..."; 或 import name from "...";
export default class ClassName { /* … */ } // import { default as name } from "..."; 或 import name from "...";
export default function* generatorFunctionName() { /* … */ } // import { default as name } from "..."; 或 import name from "...";
export default function () { /* … */ } // import { default as name } from "..."; 或 import name from "...";
export default class { /* … */ } // import { default as name } from "..."; 或 import name from "...";
export default function* () { /* … */ } // import { default as name } from "..."; 或 import name from "...";
// 导出模块合集
export * from "module-name"; // import * as name from "..."
export * as name1 from "module-name"; // import { name1 } from "...";
export { name1, /* …, */ nameN } from "module-name"; // import { name1, /* …, */ nameN } from "...";
export { import1 as name1, /* …, */ importN as nameN } from "module-name"; // import { name1, /* …, */ nameN } from "...";
export { default, /* …, */ } from "module-name"; // import defaultName, { /* …, */ } from "..."; 或 import { default as defaultName, /* …, */ } from "..."
export { default as name1 } from "module-name"; // import { name1 } from "...";
请注意,export {}
不会导出一个空对象——它是一个不导出任何东西(一个空的名称列表)的无操作声明。
导出声明不受暂时性死区规则的限制。你可以在声明名称 X
之前声明当前模块导出 X
。
export default
语法允许任何表达式。
export default 1 + 1;
作为一种特殊情况,函数和类是作为声明而不是表达式导出的,而且这些声明可以是匿名的。这意味着函数将被提升。
// 有效是因为 `foo` 是函数声明,而不是函数表达式
foo();
export default function foo() {
console.log("Hi");
}
// 从技术上讲,它仍然是一个声明,但允许匿名
export default function () {
console.log("Hi");
}
import
import
声明有四种形式:
- 具名导入:
import { export1, export2 } from "module-name";
- 默认导入:
import defaultExport from "module-name";
- 命名空间导入:
import * as name from "module-name";
- 副作用导入:
import "module-name";
具名导入
给定一个名为 myExport
的值,该值已经通过隐式方式 export * from "another.js"
从模块 my-module
导出,或者显式地使用 export
语句导出,这将把 myExport
插入到当前作用域中。
import { myExport } from "/modules/my-module.js";
你可以从同一个模块导入多个名称。
import { foo, bar } from "/modules/my-module.js";
你可以在导入时重命名导出。例如,这会将 shortName
插入当前作用域。
import { reallyReallyLongModuleExportName as shortName } from "/modules/my-module.js";
模块还可能以字符串字面量的形式导出一个成员,如果该字符串不是一个有效的标识符,那么你必须对其进行别名化,以便在当前模块中使用它。
// /modules/my-module.js
const a = 1;
export { a as "a-b" };
import { "a-b" as a } from "/modules/my-module.js";
import { x, y } from "mod"
并不等同于import defaultExport from "mod"
然后从defaultExport
中解构出x
和y
。具名导入和默认导入是 JavaScript 模块中不同的语法。
默认导入
默认导出需要使用相应的默认导入语法来导入。直接导入默认导出的最简单的版本:
import myDefault from "/modules/my-module.js";
由于默认导出没有明确指定名称,因此你可以为标识符指定任何你喜欢的名称。
也可以在使用命名空间导入或具名导入时指定默认导入。在这种情况下,必须首先声明默认导入。例如:
import myDefault, * as myModule from "/modules/my-module.js";
// myModule.default 和 myDefault 指向相同的绑定
或者
import myDefault, { foo, bar } from "/modules/my-module.js";
导入一个名为 default
的名称与默认导入效果相同。必须对其进行别名化,因为 default
是保留字。
import { default as myDefault } from "/modules/my-module.js";
命名空间导入
以下代码将 myModule
插入当前作用域,其中包含来自 /modules/my-module.js
模块的所有导出。
import * as myModule from "/modules/my-module.js";
在这里,myModule
表示一个命名空间对象,其中包含来自 /modules/my-module.js
模块的所有导出。例如,如果上面的模块导入了一个导出 doAllTheAmazingThings()
,你可以像这样调用它:
myModule.doAllTheAmazingThings();
myModule
是一个密封的对象,其原型为 null
。它提供了一个名为 default
的键,用于访问默认导出。
副作用导入
导入整个模块只是为了产生副作用,而不导入任何内容。这会运行模块的全局代码,但实际上不会导入任何值。
import "/modules/my-module.js";
这经常用于 polyfill,它会修改全局变量。
导入的值只能由导出者修改
被导入的标识符是一个动态绑定,因为它可能由导出它的模块重新赋值。但是,导入它的模块不能重新赋值。然而,任何拥有导出对象的模块都可修改该对象,并会影响到所有导入该值的模块。
你也可以通过模块命名空间对象观察到新的值。
// my-module.js
export let myValue = 1;
setTimeout(() => {
myValue = 2;
}, 500);
// main.js
import { myValue } from "/modules/my-module.js";
import * as myModule from "/modules/my-module.js";
console.log(myValue); // 1
console.log(myModule.myValue); // 1
setTimeout(() => {
console.log(myValue); // 2;my-module 更新了它的值
console.log(myModule.myValue); // 2
myValue = 3; // TypeError: Assignment to constant variable.
// 导入模块只能读取该值但不能重新对它赋值。
}, 1000);
模块命名空间对象(Module namespace object)
模块命名空间对象是描述模块所有导出的对象。它是在评估模块时创建的静态对象。有两种方法可以访问模块的模块命名空间对象:通过命名空间导入(import * as name from moduleName
),或通过动态导入的实现值。
module namespace 对象是 prototype
为 null
的 sealed
对象(密封对象是指那些不可扩展的,且所有自有属性都不可配置且因此不可删除(但不一定是不可写)的对象)。这意味着对象的所有字符串键都对应于模块的导出,并且永远不会有额外的键。所有键都可以按字典顺序枚举(即 Array.prototype.sort()
的默认行为),默认导出作为名为 default
的键可用。此外,模块命名空间对象有一个 [Symbol.toStringTag]
属性,其值为“module”,用于 object.prototype.toString()
。
每个模块说明符对应于一个唯一的模块命名空间对象,因此通常情况如下:
import * as mod from "/my-module.js";
import("/my-module.js").then((mod2) => {
console.log(mod === mod2); // true
});
除了一个奇怪的情况:因为 promise 永远不会实现到 thenable
,如果 my-module.js
模块导出一个名为 then()
的函数,那么当动态导入的 promise 实现时,该函数将自动被调用,作为 promise 解析过程的一部分。
// my-module.js
export function then(resolve) {
console.log("then() called");
resolve(1);
}
// main.js
import * as mod from "/my-module.js";
import("/my-module.js").then((mod2) => {
// Logs "then() called"
console.log(mod === mod2); // false
});
import()
import()
语法,通常称为动态导入,是一个类似函数的表达式,它允许将 ECMAScript 模块异步和动态地加载到潜在的非模块环境中。
import()
调用是一种与函数调用非常相似的语法,但 import
本身是一个关键字,而不是一个函数。你不能像 const myImport = import
那样为它设置别名,否则会引发 SyntaxError
。
import(moduleName[, options])
参数
moduleName
—— 要从中导入的模块。说明符的求值由主机指定,但始终遵循与静态导入声明相同的算法。options
—— 对象{ with: {} }
,同import attributes
。
返回值
返回一个 Promise,该 Promise:
-
如果引用的模块被成功加载和评估,则满足一个模块命名空间对象:一个包含
moduleName
所有导出的对象。 -
如果
moduleName
的字符串抛出,则 reject 并抛出错误。 -
如果
moduleName
引用不存在的模块,则拒绝并出现实现定义的错误(Node 使用泛型Error
,而所有浏览器都使用TypeError
)。 -
如果引用的模块的评估引发,则 reject 并引发错误。
什么时候需要动态导入
- 静态导入时,会显著降低代码的加载速度或增加程序的内存使用量,并且很可能需要导入的代码,或者直到以后才需要它。
- 当您导入的模块在加载时不存在时。
- 当需要动态构造导入说明符字符串时。(静态导入仅支持静态说明符)。
- 当正在导入的模块具有副作用时,除非满足某些条件,否则您不希望这些副作用。(建议在模块中不要有任何副作用,但有时您无法在模块依赖项中控制它)。
- 当您处于非模块环境 (例如,
eval
或脚本文件) 中时。
<script type="module">
中可以使用静态导入和动态导入,<script>
中只能使用动态导入。
import attributes
import attributes 功能指示运行时应如何加载模块,包括模块解析、获取、解析和评估的行为。它支持在 import
声明, export...from
声明和import()
中使用。
属性可以附加到任何类型的 import
/export from
语句,包括默认导入、命名空间导入等。它们遵循模块说明符字符串,并以 with
关键字为前缀。
import { names } from "module-name" with {};
import { names } from "module-name" with { key: "data" };
import { names } from "module-name" with { key: "data", key2: "data2" };
import { names } from "module-name" with { key: "data", key2: "data2", /* …, */ keyN: "dataN" };
export { names } from "module-name" with {};
export { names } from "module-name" with { key: "data" };
export { names } from "module-name" with { key: "data", key2: "data2" };
export { names } from "module-name" with { key: "data", key2: "data2", /* …, */ keyN: "dataN" };
参数
keyN
—— 属性键。可以是标识符或字符串文本。所有键都必须是唯一的,并且必须为运行时所知。dataN
—— 属性值。必须是字符串文本。
import data from "https://example.com/data.json" with { type: "json" };
type
属性会更改模块的获取方式(浏览器使用 Accept: application/json
标头发送请求),但不会更改模块的解析或评估方式。运行时已经知道在给定响应 MIME 类型的情况下将模块解析为 JSON。
type
属性还支持其他模块类型。例如,HTML 规范还定义了 css
类型,该类型导入 CSSStyleSheet
对象:
import styles from "https://example.com/styles.css" with { type: "css" };
import.meta
import.meta
元属性将特定上下文的元数据暴露给 JavaScript 模块。它包含了这个模块的信息,例如这个模块的 URL。import.meta
元属性在 JavaScript 模块中可用;在模块之外(包括在模块中直接调用 eval()
)使用 import.meta
是语法错误。
import.meta
是一个宿主环境创建的可扩展的 null
原型对象,其所有属性均可写、可配置、可枚举。规范没有在对象上明确定义任何属性,但是宿主环境通常会定义以下属性:
-
url
—— 到此模块的完整 URL,包括查询参数和片段标识符(在?
和#
之后)。在浏览器中,它是可获取此脚本的 URL(对外部脚本)或者是包含此脚本的文档的 URL(对内联脚本)。在 Node.js 中,它是文件路径(包括file://
协议部分)。 -
resolve
—— 将一个模块的标识符解析为相对于当前模块的 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
在脚本中引入别的脚本同样生效:
// index.mjs
import "./index2.mjs?someURLInfo=5";
// index2.mjs
new URL(import.meta.url).searchParams.get("someURLInfo"); // 5
Node.js 中 ES 模块的实现支持在解析模块的标识符中含有查询参数和片段标识符,如上述示例所示。然而,从命令行指定模块标识符时无法使用查询参数和片段标识符(例如 node index.mjs?someURLInfo=5
),因为命令行的入口点使用了类似于 CommonJS 的解析模式,将整个路径视为文件而非 URL。要将参数传递给入口点模块,需要改用命令行参数,并从 precess.argv
读取(如 node index.mjs --someURLInfo=5
)。
相对当前文件解析文件路径
在 Node.js 的 CommonJS 模块中,有专门的 __dirname
变量,值为包含当前文件的文件夹的绝对路径,可以用来解析相对路径。但是,ES 模块除了 import.meta
之外没有上下文变量。因此,要解析相对路径的文件,可以使用 import.meta.url
。注意这个属性使用的是 URL 而非文件系统的路径。
之前(CommonJS):
const fs = require("fs/promises");
const path = require("path");
const filePath = path.join(__dirname, "someFile.txt");
fs.readFile(filePath, "utf8").then(console.log);
之后(ES 模块):
import fs from "node:fs/promises";
const fileURL = new URL("./someFile.txt", import.meta.url);
fs.readFile(fileURL, "utf8").then(console.log);
<script>
HTML <script>
元素用于嵌入可执行代码或数据,这通常用作嵌入或者引用 JavaScript 代码。<script>
元素也能在其他语言中使用,比如 WebGL 的 GLSL 着色器语言和 JSON。
属性
-
async
—— 对于普通脚本,如果存在async
属性,那么普通脚本会被并行于 HTML 页面解析请求,并尽快解析和执行,会阻塞文档的解析。对于模块脚本,如果存在async
属性,那么脚本及其所有依赖都会在延缓队列中执行,因此它们会被并行请求,并尽快解析和执行。该属性能够消除解析阻塞的 Javascript。解析阻塞的 Javascript 会导致浏览器必须加载并且执行脚本,之后才能继续解析。 -
defer
—— 这个布尔属性的设置是为了向浏览器表明,该脚本是要在文档被解析后,但在触发DOMContentLoaded
事件之前执行的。包含defer
属性的脚本将阻塞DOMContentLoaded
事件触发,直到脚本完成加载并执行。本属性不应在缺少
src
属性的情况下使用(也就是内联脚本的情况下),这种情况下将不会生效。模块脚本默认是defer
的包含
defer
属性的脚本会按照它们出现在文档中的顺序执行。这个属性能够消除阻塞解析的 JavaScript。
-
crossorigin
—— 正常的script
元素将最小的信息传递给window.onerror
,用于那些没有通过标准 CORS 检查的脚本。要允许对静态媒体使用独立域名的网站进行错误记录,请使用此属性。 -
fetchpriority
—— 提供一个指示,说明在获取外部脚本时要使用的相对优先级。允许的值:-
high
—— 获取该脚本的优先级比其他外部脚本的等级要高。 -
low
—— 获取该脚本的优先级比其他外部脚本的等级要低。 -
auto
—— 默认值:自动确定获取该脚本的相对优先级。
-
-
integrity
—— 该属性允许 script 标签提供一个 hash 值,用于检验加载的 JS 文件是否完整。比如,如下便签的 integrity 值就是告诉浏览器:使用 sha256 算法计算 JS 文件的摘要签名,然后对比 integrity 值,如果不一致就不执行该资源。它的主要功能就是防止托管在 CDN 上的资源被篡改。<scrip src="//code.jquery.com/jquery.js" integrity="sha256-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC" ></scrip>
-
nomodule
—— 支持module
作为type
属性的浏览器忽略任何具有nomodule
属性的脚本。这种机制可以允许你在使用模块脚本时为不支持的浏览器提供nomodule
标记的回落脚本。<script type="module" src="main.js"></script> <script nomodule src="fallback.js"></script>
支持 module 的浏览器,设定上就不会执行 nomodule 属性的 script 脚本,所以它只会跑上方的 main.js 脚本。
而老破旧的浏览器不支持
type="module"
,会跳过这个 script 标签;同时又由于它不认识nomodule
属性,反倒会执行 nomodule script 里的fallback.js
文件。 -
nonce
—— 它是一个加密数字,需要配合 Content-Security-Policy 的script-src
使用。举个例子,http 头的 CSP 属性如下:Content-Security-Policy: script-src 'nonce-EfNBf03nceIOAn39fn389h3sdfa';
只有在 script 标签内带有相同 nonce 值的脚本才能执行:
<script nonce="nonce-EfNBf03nceIOAn39fn389h3sdfa" src="./hello.js"></script>
-
referrerpolicy
—— 表示在获取脚本或脚本获取资源时,要发送哪个referrer
:no-referrer
:不会发送Referer
标头。no-referrer-when-downgrade
(默认):如果没有 TLS(HTTPS)协议,Referer
标头将不会被发送到源上。origin
:发送的 referrer 将被限制在 referrer 页面的源:其协议、主机和端口。origin-when-cross-origin
:将会限制发送至其他源的 referrer 的协议、主机和端口号。在同源的导航上仍然包括路径。same-origin
:在同源内将发送 referrer,但是跨源请求不包含 referrer 信息。strict-origin
:只在协议安全等级相同时(如 HTTPS→HTTPS)发送文档的源作为 referrer,目标安全性降低(如 HTTPS→HTTP)时不发送。strict-origin-when-cross-origin
:在执行同源请求时,发送完整的 URL,但只在协议安全级别保持不变(如 HTTPS→HTTPS)时发送源,而在目标安全性降低(如 HTTPS→HTTP)时不发送标头。unsafe-url
:referrer 将包含源和路径(但不包含片段、密码和用户名)。这个值是不安全的,因为它将 TLS 保护的资源的源和路径泄露给不安全的源。
空字符串(
""
)既是默认值,也是在不支持referrerpolicy
的情况下的一个回退值。如果没有在<script>
元素上明确指定referrerpolicy
,它将采用更高级别的 referrer 策略,即对整个文档或域设置的策略。如果没有更高级别的策略,空字符串将被视为等同于no-referrer-when-downgrade
。 -
src
—— 这个属性定义引用外部脚本的 URI,这可以用来代替直接在文档中嵌入脚本。当存在此属性时,标签内的内容会被忽略。 -
type
—— 该属性表示所代表的脚本类型。该属性的值可能为以下类型:-
属性未设置(默认),一个空字符串,或一个 JavaScript MIME 类型:代表脚本为包含 JavaScript 代码的“传统的脚本”。如果脚本指的是 JavaScript 代码,我们鼓励作者省略这个属性,而不是指定一个 MIME 类型。所有的 JavaScript MIME 类型都列在 IANA 的媒体类型规范中。
-
module
此值导致代码被视为 JavaScript 模块。其中的代码内容会延后处理。
charset
和defer
属性不会生效。与传统代码不同的是,模块代码需要使用 CORS 协议来跨源获取。 -
importmap
此值代表元素体内包含导入映射(importmap)表。导入映射表是一个 JSON 对象,开发者可以用它来控制浏览器在导入 JavaScript 模块时如何解析模块标识符。
<script type="importmap"> { "imports": { "square": "./shapes/square.js", "circle": "https://example.com/shapes/circle.js" } } </script>
import { name as squareName, draw } from "square"; import { name as circleName } from "circle";
-
任何其他值
所嵌入的内容被视为一个数据块,不会被浏览器处理。开发人员必须使用有效的 MIME 类型,但不是 JavaScript MIME 类型来表示数据块。所有其他属性,包括
src
均会被忽略。
-
-
blocking
实验性这个属性明确指出,在获取脚本的过程中,某些操作应该被阻断。要阻断的操作必须是一个以空格分隔的列表,下面列出了阻断属性。
render
—— 页面的渲染将被阻止,直到脚本被获取和执行。
注意
没有 async
、defer
或 type="module"
属性的脚本,以及没有 type="module"
属性的内联脚本,会在浏览器继续解析页面之前立即获取并执行。
脚本应该以 text/javascript
的 MIME 类型提供,但浏览器比较宽容,只有在脚本以图像类型(image/*
)、视频类型(video/*
)、音频类型(audio/*
)或 text/csv
提供时才会阻止它们。如果脚本受阻,将向该元素发送 error
事件;否则,将发送 load
事件。