在这篇博文中,我们研究了ECMAScript提案 "JSON模块"(由Sven Sauleau, Daniel Ehrenberg, Myles Borins和Dan Clark提出)。它让我们把JSON数据像ECMAScript模块一样导入。
为什么我们要像一个模块一样导入JSON?
长期以来,各种捆绑程序(如webpack)允许我们将JSON数据像ECMAScript模块一样导入。JSON模块把这变成了一个标准功能。
为什么这很有趣呢?它提供了一种方便的方式来使用,例如,在我们的应用程序中使用配置数据。以下面这个文件结构为例:
my-app/
src/
config-data.json
main.mjs
my-app/src/config-data.json 看起来如下:
{
"appName": "My App"
}
这是my-app/src/main.mjs:
import configData from './config-data.json' assert {type: 'json'};
console.log(`I am ${configData.appName}!`);
从assert ,直到最后的语法被称为一个 import assertion.JSON模块是创建导入断言的用例之一。
一个JSON模块的默认出口包含JSON数据。没有命名的出口。
通过fetch() 获取JSON数据
如果没有JSON模块,我们将不得不使用fetch():
async function fetchConfigData(relativePath) {
const urlOfConfigData = new URL(
relativePath, import.meta.url); // (A)
const response = await fetch(urlOfConfigData.toString()); // (B)
const json = await response.json(); // (C)
return json;
}
const configData = await fetchConfigData('config-data.json');
console.log(`I am ${configData.appName}!`);
我们正在使用两个相对较新的功能:
- Fetch API,一个基于承诺的API,用于下载JavaScript代码中的文件(B行和C行)。
- 模块元数据属性
import.meta.url(A行)。
fetch() 与JSON模块相比,Fetch API有两个缺点。
- 代码稍微复杂一些。
- Node.js目前没有内置支持
fetch()。(而且我怀疑JSON模块会比fetch()更早得到支持。)
通过import() ,动态地导入JSON模块
之前的import 语句是静态的(在运行时固定)。我们也可以动态地导入JSON模块(在运行时可改变):
async function importConfigData(moduleSpec) {
// The `await` can be ommitted, but is easier to understand
return await import(moduleSpec, {assert: {type: 'json'}}); // (A)
}
const configData = await importConfigData('./config-data.json').default;
console.log(`I am ${configData.appName}!`);
请注意, import() 操作符(A行)返回一个模块命名空间对象。这就是为什么我们把属性.default (包含默认导出)分配给configData 。
你可能想知道为什么我们要在重要语句的末尾使用额外的语法:
import configData from './config-data.json' assert {type: 'json'};
为什么JavaScript不能通过查看文件名的扩展名来检测这是JSON?
import configData from './config-data.json';
这是不可能的,因为这可能会引起安全问题。浏览器从来不看文件名扩展名,他们看的是内容类型。而服务器是负责为文件提供内容类型的。因此,在导入一个.json 文件时,可能会发生两件事:
- 我们自己的服务器可能会发送一个不同于
application/json的内容类型,而导入工作会在某些方面出错。 - 对于外部服务器,事情就更危险了:如果它发送的文件的内容类型是
text/javascript,它就可能在我们的应用程序中执行代码。
因此,JavaScript在导入JSON时不会依赖内容类型。