Node.js动态导入JSON文件缓存导致返回旧值无法更新问题
问题原因
加时间戳清缓存的写法不生效,核心是两个认知偏差:
- 你参考的追加查询参数绕过缓存的方案,只对网络地址的ESM导入有效,本地文件模块根本不吃这一套。Node处理本地模块时,缓存键是文件的真实绝对路径,你直接在path拼接的字符串后面加
?xxx=xxx,要么会被当成文件名的一部分去找不存在的文件,要么被加载器直接把查询串截掉,永远命中同一个缓存条目,当然拿不到新值。 path.join拼出来的是操作系统认的文件路径,不是ESM动态导入要求的file://协议URL,传非标准路径给import()本身就属于不规范用法,行为全靠加载器容错,出现缓存不刷新的问题完全符合预期。
说白了,import()天生是用来加载静态模块的,默认带永久缓存,从设计上就不适合读运行时会反复修改的文件。
解决方案
方案1:文件系统API直接读取(生产环境首选)
完全绕开ESM模块缓存逻辑,每次请求直接读取磁盘上的最新文件内容解析返回,行为100%可控,没有缓存、内存泄漏之类的幺蛾子。
改造步骤:
- 新增引入fs promise API:
import { readFile } from 'fs/promises';
- 替换原有的
loadJSON函数:
const loadJSON = async () => { const filePath = path.join(__dirname, 'values.json'); const fileContent = await readFile(filePath, 'utf-8'); return JSON.parse(fileContent); };
改完后不需要加任何特殊启动参数,只要values.json文件修改保存,下一次请求就能拿到最新内容。
方案2:构造合法file URL绕过缓存(仅适合低频调试场景)
如果一定要用动态import加载JSON,需要先把本地路径转为标准file://协议URL,再追加时间戳参数,让Node把带不同参数的地址识别为全新模块,绕过原有缓存。
注意:这个方案存在明确的内存泄漏问题——每次生成的带新时间戳的模块都会永久驻留在ESM缓存中无法被回收,接口QPS高的话服务内存会持续上涨,绝对不要在生产环境用。
改造步骤:
- 新增引入路径转URL的工具方法:
import { pathToFileURL } from 'url';
- 替换原有的
loadJSON函数:
const loadJSON = async () => { const filePath = path.join(__dirname, 'values.json'); const jsonUrl = pathToFileURL(filePath); jsonUrl.searchParams.set('update', Date.now()); const values = await import(jsonUrl.href); return values.default; };
补充说明
你启动服务时加的--experimental-json-modules参数在Node 18及以上版本已经不需要了,JSON模块导入已经成为稳定特性,但不管版本新旧,都不建议用动态import读取运行时会变更的JSON文件。
内容的提问来源于stack exchange,提问作者Eyal Solomon
相关产品推荐
相关产品推荐

