Node.js:小脚本读 JSON 配置,`fs/promises` 加 `JSON.parse` 就行

71 次浏览7 条回复

有些本地小工具只需要读一个配置文件,不一定要先引入额外库。Node 自带的 fs/promisesJSON.parse 就能处理最简单的场景。

环境:Node.js 18+。可以新建两个文件:

// config.json
{
  "name": "demo",
  "port": 3000
}
// read-config.mjs
import { readFile } from 'node:fs/promises'

const text = await readFile('./config.json', 'utf8')
const config = JSON.parse(text)

console.log(config.name)
console.log(config.port + 1)

然后跑:

node read-config.mjs

会看到:

demo
3001

这个只适合可信的本地配置。要是配置来自用户上传或者远端接口,还是要再做字段校验,不然读出来的结构不一定是代码想要的样子。

这里再补一个小习惯:读完之后最好把 JSON.parse 包一层报错提示,不然配置文件少个逗号时,用户只看到一串语法错误会有点懵。

比如本地 CLI 里可以这样:

let config
try {
  config = JSON.parse(text)
} catch (err) {
  throw new Error(`配置文件不是合法 JSON: ${err.message}`)
}

小脚本不一定要上完整 schema,但把“文件坏了”和“字段不对”分开提示,排查会舒服很多。

还有个容易踩的小点:readFile 自己也可能失败,跟 JSON.parse 的错误分开处理会更清楚。

比如配置路径写错时,给一句更直白的提示:

let text
try {
  text = await readFile('./config.json', 'utf8')
} catch (err) {
  if (err.code === 'ENOENT') {
    throw new Error('找不到 config.json,请先创建配置文件')
  }
  throw err
}

这样文件不存在、JSON 写坏、字段不符合预期,三类问题就不会全混在一起了。

如果脚本可能从别的目录启动,还可以注意一下路径问题。readFile('./config.json') 是按当前工作目录找,不是按脚本文件所在目录找。

想固定跟脚本放一起的话,可以这样写:

import { readFile } from 'node:fs/promises'
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'

const here = dirname(fileURLToPath(import.meta.url))
const text = await readFile(join(here, 'config.json'), 'utf8')

本地随手跑没啥感觉,做成 npm script 或 CLI 之后这个差别就挺明显。

还有一种更偷懒的写法是直接 import JSON,不过这个在不同 Node 版本里细节有点变来变去。

小脚本为了少踩版本坑,我反而更喜欢这种 readFile + JSON.parse。路径、报错、字段兜底都能自己控住一点,后面要加校验也比较自然。

有个很小的细节:示例里 json 代码块第一行的 // config.json 只是标文件名,真正复制到 config.json 时别带进去。

标准 JSON 不支持注释,带上这行的话 JSON.parse 会直接炸。文件名如果想写清楚,放在代码块外面会更不容易误复制。

如果只是本地配置,也可以顺手给个默认值,别让缺字段的时候一路变成 undefined

比如:

const port = Number(config.port ?? 3000)
if (!Number.isInteger(port)) {
  throw new Error('port 需要是整数')
}

这种不算完整校验,但对小脚本已经能挡住不少手滑。

ZiorLv1#6

如果只是本地配置,也可以顺手给个默认值,别让缺字段的时候一路变成 undefined

比如:

const port = Number(config.port ?? 3000)
if (!Number.isInteger(port)) {
  throw new Error('port 需要是整数')
}

这种不算完整校验,但对小脚本已经能挡住不少手滑。

再补个边角情况:有些编辑器保存的 JSON 文件前面可能带 UTF-8 BOM,JSON.parse 会把开头那个隐藏字符也当内容看。

小脚本里如果想兜一下,可以读完后先去掉:

const text = (await readFile('./config.json', 'utf8')).replace(/^\uFEFF/, '')
const config = JSON.parse(text)

不常见,但配置文件从 Windows 环境拷过来时偶尔能省一点排查时间。