新的 导入断言 功能允许模块导入语句在模块标识符旁边包含附加信息。该功能的初始用途是使 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 模块脚本 也即将推出,使用相同的模块类型断言语法。
导入断言支持 #
- Chrome: 自版本 91 起支持
- Firefox: 不支持
- Safari: 不支持
- Node.js: 不支持
- Babel: 支持