前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库ESM 导入 JSON import 断言作用
JavaScript模块与语言机制

原生 ESM 怎么导入 JSON?导入语句后面的 with 属性起什么作用?

写成 import data from "./data.json" with { type: "json" }。with 后面的导入属性告诉宿主「我要的是 JSON」,宿主会校验,对不上就报错,不会把它当脚本执行。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#模块与语言机制
先看核心答案读代码示例
理解线索

导入属性管的是「按什么类型处理」

  1. type: "json"宿主按 JSON 解析,内容类型不符就拒绝加载
  2. 默认导出整个 JSON 解析结果就是 default,没有具名导出
  3. 模块缓存同一个 JSON 只解析一次,所有导入方拿到同一个对象

打包器里不写 with 也能导入 JSON,那是 Webpack、Vite 自己的能力。代码一旦跑在浏览器原生 ESM 或 Node 里,缺了这个属性就会直接报错。

核心回答

先记住这个答案

原生 ESM 导入 JSON 的写法是 import data from "./data.json" with { type: "json" },with 后面这段叫导入属性。它的作用是让宿主(浏览器或 Node.js)按 JSON 来处理这个文件,并且校验对方返回的内容类型,不是 JSON 就报错。导入后 data 就是解析好的对象,JSON 模块只有默认导出。早期提案写的是 assert { type: "json" },后来改成了 with,新代码只写 with。

  • JSON 模块导入需用 with 声明类型
  • 这是安全校验:防止服务端返回 JS 被当成代码执行
  • JSON 模块只有默认导出,不能按字段具名导入
  • 旧 assert 写法应迁移到 with

写法和导入结果

在 Node.js 里建一个 data.json,内容是 {"name":"demo","n":1},然后在 .mjs 文件里导入它。

静态导入和动态导入两种写法JavaScript
// main.mjs
import config from './data.json' with { type: 'json' }
console.log(config.name) // demo

// 动态导入时,属性放在第二个参数的 with 里
const mod = await import('./data.json', { with: { type: 'json' } })
console.log(Object.keys(mod)) // [ 'default' ]
console.log(mod.default === config) // true,同一个模块只解析一次

// 不写 with 会报错:ERR_IMPORT_ATTRIBUTE_MISSING
// import bad from './data.json'

动态导入拿到的是模块命名空间对象,JSON 内容挂在 default 上。两次导入拿到的是同一个对象,因为模块只会加载和解析一次。所以如果某处代码改了 config.name,其它导入方也会看到改动,拿 JSON 当配置时不要随手改它。

为什么非要多写这一段

浏览器决定怎么处理一个模块,看的是服务器返回的 MIME 类型,而不是文件扩展名。设想你从第三方 CDN 导入一个 data.json,如果对方被攻破,返回的内容类型是 text/javascript,没有任何校验的话浏览器就会把它当脚本执行,一个本该只是数据的导入变成了任意代码执行。

加上 type: "json" 以后,意图写在了导入语句里。宿主拿到响应先核对类型,不是 JSON 就拒绝,最多只会得到一个加载错误。所以导入属性是安全机制,不是给打包器看的注释。

反过来也成立:给一个普通 JS 模块写 with { type: "json" },Node.js 会报 ERR_IMPORT_ATTRIBUTE_TYPE_INCOMPATIBLE,类型对不上两边都拦。

assert 为什么被换成了 with
最早的提案叫 import assertions,语义是「只断言,不影响加载方式」。后来发现宿主确实需要根据类型改变加载行为,比如浏览器请求 JSON 模块时要带不同的请求头,「断言」这个说法就不准确了,于是改名为 import attributes,关键字换成 with。我在 Node 22 上实测,assert 写法已经是语法错误。老项目升级 Node 后如果报 Unexpected identifier 'assert',就是这个原因。
回答前,多想一步

容易答错的地方

以为可以按字段具名导入
import { name } from "./data.json" with { type: "json" } 会报错,提示模块没有名为 name 的导出。JSON 模块只有一个默认导出,要先拿到整个对象再解构。在 Webpack 里具名导入 JSON 字段可能能用,那是打包器的扩展,换到原生环境就不行了。
以为 with 只是给 TypeScript 或打包器看的
它是 ECMAScript 语法,运行时真的会校验。TypeScript 只负责类型检查,是否允许导入 JSON 还要看 resolveJsonModule 等编译选项。代码最终在浏览器或 Node.js 里跑时,缺少或写错导入属性都是运行时错误。
把 JSON 模块当成每次读取的新数据
JSON 模块和 JS 模块一样被缓存。服务器上的文件后来变了,当前页面拿到的还是第一次加载的内容。需要实时数据时用 fetch 请求接口,不要用模块导入。
试着用自己的话回答

面试官还会怎么问?

除了 JSON,导入属性还能写什么类型?

规范本身只定义了 with 语法,具体支持哪些类型由宿主决定。JSON 模块是目前最普遍的;浏览器还有 CSS 模块脚本,写 with { type: "css" } 会得到一个 CSSStyleSheet 对象,但各浏览器支持程度不一,用之前要查兼容性。

Node.js 里用 CommonJS 怎么读 JSON?

CommonJS 直接 require("./data.json") 就行,不需要任何属性,结果同样会被缓存。在 ESM 里如果想用这种方式,可以用 createRequire(import.meta.url) 造一个 require 出来。

打包项目里需要写 with 吗?

Webpack、Vite 都能直接导入 JSON,不写也能用。但如果代码会被原生 ESM 直接加载,比如发布到 npm 的库、Node.js 脚本、<script type="module"> 直接引用,就必须写,否则运行时报错。写上也不影响打包器,所以库作者通常会写。

从一道题,走向一组知识

把知识连起来

模块与语言机制

import map 是干什么的?有了它浏览器怎么解析 import 'vue'?

import map 管的是说明符映射到哪个 URL,导入属性管的是拿到文件后按什么类型处理,两者分工不同。

模块与语言机制

import.meta.resolve 是干什么的?它和 import 有什么关系?

想只拿到 JSON 文件的 URL 而不加载它,可以用 import.meta.resolve 解析路径。

参考资料

  • JavaScript:模块与语言机制参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 写法和导入结果
  3. 为什么非要多写这一段
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑