PAYATHON 2026

如何在 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 为前提。

备注:内容仅供参考。