导入属性
注意:此提案的先前版本使用assert
关键字而不是with
。断言功能现在是非标准的。有关详细信息,请查看浏览器兼容性表。
导入属性功能指示运行时如何加载模块,包括模块解析、获取、解析和评估的行为。它在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" };
参数
描述
导入属性告诉运行时如何加载特定模块。
主要用例是加载非 JS 模块,例如 JSON 模块和 CSS 模块。考虑以下语句
import data from "https://example.com/data.json";
在 Web 上,每个导入语句都会导致一个 HTTP 请求。然后,响应被准备成 JavaScript 值,并由运行时提供给程序。例如,响应可能如下所示
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
...
{"name":"John"}
模块仅根据其提供的MIME 类型进行识别和解析 - URL 中的文件扩展名不能用于识别文件类型。在这种情况下,MIME 类型为application/json
,它告诉浏览器该文件是 JSON 并且必须作为 JSON 解析。如果由于某种原因(例如服务器被劫持或伪造),服务器响应中的 MIME 类型设置为text/javascript
(用于 JavaScript 源代码),则该文件将被解析并作为代码执行。如果“JSON”文件实际上包含恶意代码,则import
声明会意外地执行外部代码,从而构成严重的安全威胁。
导入属性通过允许作者明确指定如何验证模块来解决此问题。例如,上面缺少属性的导入语句实际上会失败
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/json". Strict MIME type checking is enforced for module scripts per HTML spec.
相反,您必须提供一个属性来告诉运行时此文件必须包含 JSON。要验证模块的类型(通过 MIME 类型),请使用名为type
的属性键。要验证模块是 JSON 模块,则值为"json"
。
注意:实际的type
属性值并不直接对应于 MIME 类型。它由HTML 规范单独指定。
因此,上面的代码应重写为
import data from "https://example.com/data.json" with { type: "json" };
type
属性更改了模块的获取方式(浏览器使用
标头发送请求),但不更改模块的解析或评估方式。鉴于响应 MIME 类型,运行时已经知道将模块解析为 JSON。它仅使用该属性来进行事后检查,以确保Accept
: application/jsondata.json
模块实际上是 JSON 模块。例如,如果响应标头更改为Content-Type: text/javascript
,则程序将失败,并出现与上述类似的错误。
规范明确指出支持type: "json"
- 如果断言模块为type: "json"
并且运行时未使此导入失败,则必须将其解析为 JSON。但是,否则没有行为要求:对于没有type: "json"
属性的导入,如果此环境中没有安全问题,运行时可能仍然将其解析为 JSON。另一方面,浏览器隐式地假设该模块是 JavaScript,如果该模块不是 JavaScript(例如 JSON),则会失败。这确保了始终严格验证模块类型,并防止任何安全风险。实际上,Node 和 Deno 等非浏览器运行时与浏览器语义保持一致,并对 JSON 模块强制执行type
。
type
属性还支持其他模块类型。例如,HTML 规范还定义了css
类型,它导入CSSStyleSheet
对象
import styles from "https://example.com/styles.css" with { type: "css" };
属性语法旨在可扩展 - 尽管语言仅指定了type
,但运行时可以读取和处理其他属性。属性可以在模块加载过程的每个阶段更改运行时的行为
- 解析:属性是模块说明符(
from
子句中的字符串)的一部分。因此,在给定相同的字符串路径的情况下,不同的属性可能导致加载完全不同的模块。例如,TypeScript 支持resolution-mode
属性。tsimport type { TypeFromRequire } from "pkg" with { "resolution-mode": "require" };
- 获取:例如,CSS 模块使用
设置为destination
"style"
来获取,而 JSON 模块使用destination: "json"
来获取。这意味着在给定相同的目标 URL 的情况下,服务器仍然可以返回不同的内容。 - 解析和评估:运行时可以使用属性来确定如何解析和评估模块。
但是,您不能使用未知属性 - 如果运行时遇到未知属性,它会抛出错误。
示例
使用 type 属性导入 JSON 模块
在data.json
中
{
"name": "John"
}
在index.html
中
<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<script type="module">
import data from "./data.json" with { type: "json" };
const p = document.createElement("p");
p.textContent = `name: ${data.name}`;
document.body.appendChild(p);
</script>
</head>
<body></body>
</html>
启动本地 HTTP 服务器(请参阅故障排除)并转到index.html
页面。您应该在页面上看到John
。
注意:JSON 模块只有一个默认导出。您不能从中进行命名导入(例如import { name } from "data.json"
)。
规范
规范 |
---|
导入属性 # prod-WithClause |
浏览器兼容性
BCD 表仅在浏览器中加载