在 ESM 中导入 JSON 文件
有时候,我们需要在 Node.js 的 ES 模块中处理 JSON 文件。但是,当你试图直接在 ES 模块中导入一个 JSON 文件时,Node.js 会抛出一个错误:TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".json"
。这是因为 Node.js 在使用 import
语句时,默认期望的是 JavaScript 代码。
目前有两种常规解决方案,和一种实验性解决方案。
常规方案
使用 fs 模块读取和解析 JSON 文件
Node.js 建议使用 fs 模块并自己完成读取文件和解析的工作。其实就是使用 fs 模块读取 JSON 文件,然后再用
JSON.parse
进行解析。jsimport { readFile } from 'fs/promises' const json = JSON.parse( await readFile(new URL('./some-file.json', import.meta.url)) )
利用 CommonJS 的
require
方法加载 JSON 文件createRequire
允许你构造 CommonJS 的require
方法,以便可以使用典型的 CommonJS 功能,例如在 Node.js 中读取 JSON ES 模块。jsimport { createRequire } from 'module' const require = createRequire(import.meta.url) const data = require('./data.json')
这两种方案的区别在于:
require
机制情况下,如果多个模块都加载了同一个 JSON 文件,那么其中一个模块改变了 JS 对象,其他模块中的 JS 对象会跟着改变。这是由 Node 模块的缓存机制造成的,只有一个 JS 对象。第一种方式可以随意改变加载后的 JS 变量,而且各模块互不影响,因为它们都是独立的,是多个 JS 对象。
实验性方案
由于 JSON 模块的提议,允许使用常规的 import
语句在 ES 模块中导入 JSON 数据。你可以通过添加导入断言来表明你要导入的数据类型为 JSON。
import jsonObject from './file.json' assert { type: 'json' }
assert { type: 'json' }
是一个导入断言,指示模块应该被解析和导入为 json。jsonObject
变量包含解析 file.json
的内容后创建的 JavaScript 对象。
一个 JSON 模块的内容是使用默认导入的,命名的导入不可用。
JSON 模块也可以动态导入:
const { default: jsonObject } = await import('./file.json', {
assert: {
type: 'json'
}
})
当一个 JSON 模块被动态导入时,包括一个 JS 对象,默认的内容在该对象的 default
属性中。