如何在 JS 中导入 JSON 文件并兼容新旧浏览器?
可以兼容。代码需要同时运行在新旧 Chromium 浏览器中时,建议保留 .json 文件,用 fetch() 加载,再通过 response.json() 读取数据,避开 assert 和 with 的语法差异。
如果必须使用静态 import,可以继续用现有的 JS 模块方案。题述两类浏览器无法识别同一种静态 JSON 导入写法。
为什么 try...catch 无法解决
下面两种写法都是静态导入声明:
import people from './people.json' assert { type: 'json' };
import people from './people.json' with { type: 'json' };
assert 对应旧的 Import Assertions,with 对应后来的 Import Attributes。浏览器必须支持相应语法,才能解析代码。这次调整还涉及模块加载语义,不能把两个关键字简单地当作别名,具体可参见 V8 的说明。
静态 import 只能写在模块顶层,不能放进 try、if 或函数体中。浏览器遇到不认识的语法时,会在执行代码前解析失败,同一个文件里的 catch 也就没有机会运行。MDN 的 import 文档说明了这些限制。
动态 import() 可以放进 try...catch,但仍需考虑浏览器是否支持它使用的导入选项。如果只是读取 JSON,直接用 fetch() 更容易维护。
保留 JSON 文件,用 fetch() 读取
假设目录如下:
index.html
js/
main.js
people.json
people.json 保持为标准 JSON:
{
"people": [
{
"name": "Alice",
"age": 30
}
]
}
在 main.js 中加载:
async function loadPeople() {
const url = new URL('./people.json', import.meta.url);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`加载 people.json 失败:HTTP ${response.status}`);
}
return response.json();
}
async function main() {
try {
const people = await loadPeople();
// 在数据加载完成后使用它
console.log(people);
} catch (error) {
console.error('读取人员数据失败:', error);
}
}
main();
HTML 通过模块脚本加载它:
<script type="module" src="./js/main.js"></script>
这段代码没有使用 JSON 模块导入语法,也不依赖顶层 await。所需的基础能力在题述的 2022 年 Chromium 浏览器之前就已支持,实际部署时仍应按机器上安装的具体版本验收。
使用时需要注意:
fetch()收到 HTTP 404、500 等响应时,通常不会自动让 Promise 拒绝,因此需要检查response.ok。response.json()也返回 Promise。如果响应内容不是合法 JSON,解析就会失败,示例中的catch会处理这个错误。
具体行为可参见 MDN 的 Fetch 使用说明。
注意相对路径和异步执行
在页面环境中,如果直接写:
fetch('./people.json');
相对路径通常按页面的基准 URL 解析,不能默认它相对于当前 JS 文件。
如果希望沿用原先 import './people.json' 的路径含义,应使用:
new URL('./people.json', import.meta.url);
import.meta.url 指向当前模块的 URL,因此示例读取的是 js/people.json。这一写法只能用于模块,普通脚本不能直接照搬,具体可参见 MDN 的 import.meta 文档。
改用 fetch() 后,数据需要异步获取。依赖数据的初始化代码应放在 await 之后,调用 loadPeople() 后不能立刻假定数据已经可用。
必须使用静态导入时,继续使用 JS 模块
如果数据随应用发布,更新频率也不高,可以继续采用现有方案:
// people.js
export const PEOPLE = {
people: [
{
name: 'Alice',
age: 30
}
]
};
// main.js
import { PEOPLE } from './people.js';
console.log(PEOPLE);
这种方式只要求浏览器支持普通 ES 模块,不涉及 JSON 导入属性。代价是数据文件变成了可执行的 JavaScript。如果其他程序也需要读取原始 JSON,可以保留 JSON 作为数据源,在构建时生成对应的 JS 模块。
部署时还要检查什么
页面和 JSON 文件应通过 HTTP 或 HTTPS 提供。双击 HTML、使用 file:// 打开时,fetch() 和模块加载不一定能正常工作。
如果 JSON 来自其他域名,服务器需要正确配置 CORS。设置 mode: 'no-cors' 也无法让脚本读取原本受跨域限制的 JSON 响应,具体可参见 Fetch 文档。
File System Access API 的支持情况需要单独检查。选择 fetch() 或 JS 模块加载数据,不会改变浏览器对该 API 的支持情况;采用这两种加载方式也不需要以升级浏览器或 OS 为前提。
备注:内容仅供参考。
