
后端接口还在排期前端页面已经堆到下周这是做前端最常遇到的局面。我自己的习惯是不等后端直接在本地起一个 JSON Server把假数据塞给前端页面接口该调的调、字段该对的核对等到后端就绪再一键切换真实地址。这篇文章就是我用 JSON Server 做前端 mock 假数据工具的完整记录包含安装、路由规则、数据关联、中间件扩展和一堆踩坑后的排查经验想快速搭一套本地模拟接口环境的同学可以直接照抄。1. 为什么选了 JSON Server 而不是其他 mock 方案1.1 前端 mock 的几条常见路子先聊一下我试过的几种 mock 思路方便你判断 JSON Server 在你项目里是不是最合适的选择。第一种是“写死数据”。直接在组件文件里定义几个常量数组页面先跑起来再说。这个方式最快几秒钟就能让页面出效果但问题也最明显接口不存在、HTTP 状态码不存在、网络延迟不存在、出错场景也不存在页面调接口的完整链路完全没被验证。等你换成真实接口时才发现数据格式对不上、请求方式写错了、加载状态的时机不对等于前端只完成了一半工作。第二种是用 Mock.js 拦截 XHR 或 Ajax 请求在前端运行时把请求“半路截胡”返回随机生成的数据。Mock.js 的好处是数据生成能力强可以随机出几十条甚至上百条假数据而且不用额外起服务。缺点也同样明显它拦截的是前端代码发出的请求这些请求并没有真正经过 HTTP 链路前端看到的效果接近真实但网络层是不可见的。它的机制决定了不适合联调也不适合模拟多种接口异常情况在切换到真实环境的代码改动也偏多。第三种就是本文主角 JSON Server。它是一个独立的 Node.js 服务启动后监听一个端口把 JSON 文件里的数据自动编译成一套符合 RESTful 风格的接口。前端把 baseURL 指向这个服务发 HTTP 请求、收 HTTP 响应和调真实接口没什么区别。你在浏览器 Network 面板里能看到完整的请求记录和响应体也能用 Postman 或 curl 单独调试这种体验比 Mock.js 更接近真实开发。第四种是团队协作平台方案比如 YApi、Rap2、Apifox 那一类工具。它们能基于接口文档生成 mock 数据还能多成员共享适合团队内部统一管理接口。但这类工具一般需要独立部署或者依赖云端个人本地想快速起一套轻量 mock 还是有点重交给团队用可以个人日常开发我还是倾向 JSON Server 这种命令行就能跑的工具。1.2 JSON Server 适合哪些场景不适合哪些场景我用下来的感受是JSON Server 最适合的场景是“个人开发环境下的接口自测”。比如你负责一个模块后端接口还未定义完成你只需要按约定的接口路径 mock 一份数据让页面能调通、能展示、能提交这个工具简直是为此而生。它同样适合前端做演示 Demo、做原型验证、写前端单元测试和集成测试时模拟后端返回。如果你在做一些独立性较强的 H5 活动页或组件库演示用它搭一个临时接口服务也很顺手。但它有几个典型的能力边界。第一JSON Server 本质上只能对 JSON 文件做增删改查没有数据库事务没有多表联查复杂业务逻辑是写不进去的。第二它没有用户体系没有状态保持登录态、权限体系都得靠自定义中间件补齐。第三真实后端往往有文件上传、WebSocket 推送等能力JSON Server 默认支持不了需要自己写额外的中间件处理。第四别忘了它是本地开发工具不能当作生产环境服务使用我没有见过谁会把 JSON Server 直接部署到服务器供线上访问这是安全红线容易把数据整个暴露出去。2. 环境准备与快速启动从安装到跑通第一个接口2.1 安装方式与版本差异JSON Server 是 Node.js 生态下的工具安装之前确保本机已经有 Node.js 环境。它的安装方式分两种一种是在项目里作为开发依赖安装另一种是全局安装。我个人建议放在项目里装开发依赖也就是npm install json-server --save-dev。为什么这么建议因为全局工具对你的团队同事不起作用别人拉下项目还要重新全局装一遍而放在 devDependencies 里任何一个新同学执行npm install就能获得一致的 mock 环境版本也统一可控。安装的时候留意一下版本。目前使用最多的还是 0.17.x 这个稳定版本很多教程、博客也都是基于这个版本写的。新版 1.x 也推出了 beta 版命令和配置有一些变化但核心思路基本一致。如果你跟着网上教程操作时发现某些参数不对建议先检查一下当前安装的版本再用json-server --help查看实际支持的参数列表。如果你只想临时测试一下不想装任何东西也可以用npx json-server db.json这种方式。npx 会临时拉取并执行 json-server完事就走不留包适合我第一次接触这个工具时的验证场景。2.2 第一个 db.json 文件与启动命令JSON Server 的数据载体是 JSON 文件。我习惯给它起名叫db.json放在项目根目录或mock目录下。它的格式有一个关键约定顶层每个 key 代表一类资源value 是资源数组每个数组元素就是一个对象对象里必须有唯一 id 字段。下面这个例子非常典型{ users: [ { id: 1, name: 张三, email: zhangsanexample.com }, { id: 2, name: 李四, email: lisiexample.com } ], posts: [ { id: 1, title: JSON Server 使用笔记, userId: 1, views: 1024 }, { id: 2, title: 前端 mock 假数据工具对比, userId: 2, views: 512 } ] }保存这个文件后在终端执行npx json-server --watch db.json --port 3000--watch参数的意思是监听 db.json 文件变化你手动往文件里加一条数据服务会自动重载不用反复重启这是我最常用的参数。启动成功后终端会把可访问的路由全部打印出来。默认访问地址是http://localhost:3000所以你打开浏览器访问http://localhost:3000/posts就能看到 posts 的完整数组访问http://localhost:3000/users/1就能看到 id 为 1 的用户对象。到这一步你已经拥有了一个最小可用的 mock 后端。2.3 常用启动参数和配置文件JSON Server 的命令行参数不多但每一个都挺实用。我挑几个常用的列出来--port指定监听端口默认 3000。端口被占用时用这个参数快速换端口。--host指定监听地址默认 localhost。真机调试或局域网访问时改成0.0.0.0。--delay给所有响应统一加延迟毫秒数比如--delay 500模拟真实网络慢的场景。--routes指定一个路由映射配置文件可以自定义 URL 路径。--middlewares指定自定义中间件文件列表用于扩展功能后面章节会展开。--static指定静态资源目录相当于同时托管一个静态文件服务。--no-cors关闭跨域相关响应头一般不建议使用。routes.json 文件算是我后期用得越来越多的功能。默认情况下 JSON Server 的路由完全由 db.json 的顶层 key 决定但真实后端的接口路径往往不是这么直接。比如后端接口可能是/api/v1/posts而 JSON Server 默认是/posts。routes.json 可以把路径做一个映射配置如下{ /api/*: /$1, /v1/posts: /posts }写法上要留意routes.json 里的 key 是匹配请求路径的模式value 是实际的路由名称。配置好之后启动命令变为json-server --watch db.json --routes routes.json。这在实际项目里可以直接对齐后端接口文档的路径减少前端切换时的改动量。3. 数据查询、关联与写操作的完整实操3.1 查询参数详解过滤、分页、排序、模糊搜索JSON Server 把 REST API 最常用的查询场景都封装成了 URL 参数不需要写任何后端逻辑就能直接用这是它效率很高的原因之一。精确过滤是最基础的。比如GET /posts?userId1会只返回 userId 等于 1 的帖子。多个条件同时存在时默认是 AND 关系GET /posts?userId1views100会返回同时满足两个条件的记录。如果你要看某个字段是否等于某个值直接用字段名加值就行。需要判断不等于时用_ne操作符比如views_ne100表示 views 不等于 100。范围查询用_gte和_lte操作符它们是大于等于和小于等于的意思。比如查看浏览量在 100 到 500 之间的帖子可以写成GET /posts?views_gte100views_lte500。这个参数在筛选时间范围内的数据时特别好用比如按创建时间范围过滤。分页也是高频需求JSON Server 用_page和_limit两个参数控制。GET /posts?_page1_limit10表示取第一页的 10 条数据。这里有一个容易忽略的重要细节分页后的数据量在响应头X-Total-Count中才能拿到表示总记录数前端用 axios 等库时需要通过response.headers[x-total-count]读取。很多前端同事在本地拿到数组后直接渲染完全没注意到总页数其实藏在响应头里导致做分页组件时卡壳。排序用_sort和_order比如GET /posts?_sortviews_orderdesc按浏览量倒序返回多字段排序时可以写成_sortviews,id_orderdesc,asc。模糊搜索同样内置了参数是q它会匹配所有字符串字段。GET /posts?qjson会返回标题、内容等任意字段中包含 “json” 一词的记录。注意q是全局搜索不需要指定具体哪个字段这在日常调试时非常顺手。此外 JSON Server 还支持_start和_end做切片查询类似数组的 slice 行为适合对位置敏感的分页逻辑。3.2 关联数据查询_embed 与 _expand 的正确打开方式真实业务里数据很少是孤立的一个帖子挂在多个评论一个用户发布多个帖子。JSON Server 实现关联数据靠的是外键约定理解了这个约定_embed和_expand就能用得很顺畅。先看数据设计。评论数据通常长这样{ posts: [ { id: 1, title: JSON Server 使用笔记, userId: 1 } ], comments: [ { id: 1, text: 写得好, postId: 1 } ] }评论对象里有一个postId字段这就是外键。JSON Server 识别外键的规则对命名很挑剔它默认按“资源名单数 Id”的驼峰格式推断也就是说父资源是 posts外键就叫 postId父资源是 users外键就叫 userId。如果你的数据模型里写的是其他命名字段比如article_id或者authorId但父资源是 usersJSON Server 都没法自动关联查询结果就是空的这个问题我踩过不止一次。_embed的作用是把子资源嵌入到父资源里一起返回。访问GET /posts?_embedcomments返回的每个 post 对象会多出一个comments数组里面是该帖子的所有评论。访问GET /posts/1?_embedcomments则是只看 id 为 1 的帖子并附带评论。简单理解_embed是“往下查询子资源”。_expand的作用相反是把父资源展开到子资源里。访问GET /comments?_expandpost返回的每条评论会多出一个post对象包含评论所属帖子的完整信息。访问GET /comments/1?_expandpost同理。注意_expand后面的参数必须是单数形式比如 post 而不是 posts。在实际项目里这两个参数用起来非常方便。比如前端帖子列表页需要展示评论数量只需请求GET /posts?_embedcomments然后在前端取comments.length即可不用再发一个统计接口。详情页需要同时展示帖子和用户信息也可以一次请求GET /posts/1?_expanduser搞定前提是 posts 里有 userId 外键。这样能大大减少前端的并发请求数开发效率提升明显。3.3 写操作POST、PUT、PATCH 与 DELETE 的细节JSON Server 不只支持查询它还实现了完整的增删改查接口这也是它能支撑前端整个业务流程测试的根本原因。新增数据用 POST比如POST /posts请求体传 JSON{ title: 新增的文章, userId: 1 }服务会返回状态 201并在响应体里带上新创建的对象。关键是 id 的生成规则如果 db.json 中当前最大 id 是 5新记录的 id 就是 6顺序递增。这个行为符合大多数后端的生成逻辑前端可以直接用返回对象里的 id 做后续跳转或关联操作。但要注意一点如果你 POST 请求体里手动带了 idJSON Server 可能会忽略它而自行生成所以不要指望前端指定 id 能生效。更新数据用 PUT 或 PATCH。PUT 是整体替换请求体会把目标对象完全覆盖要求你把所有字段都传全比如PUT /posts/1就必须传id、title、userId等所有字段。PATCH 是部分更新只修改传入字段PATCH /posts/1只传{ views: 999 }就能只更新浏览量。日常开发中我更常用 PATCH因为后端接口大多也是部分字段更新的语义。删除数据用 DELETEDELETE /posts/1会删除 id 为 1 的帖子成功后返回 200 和空对象。这里要特别提醒一个容易踩坑的行为JSON Server 的写操作会直接修改磁盘上的 db.json 文件。也就是说你前端页面上点了几下删除按钮db.json 里的数据就真的没了。这在调试时很方便但如果你只是想做演示可能不小心把精心准备的数据删掉一大半。我的建议是把 db.json 纳入 Git 版本管理或者在跑之前复制一份原始数据改乱了随时恢复。3.4 数据设计建议资源拆分与字段命名规范使用 JSON Server 一段时间后我发现 mock 数据的设计质量直接影响整套工具的体验。这里分享几条自己总结的实践规范。第一按实体拆分资源不要全塞一个大数组。我见过有人在 db.json 里只放一个datakey把所有对象堆一起让 JSON Server 生成一堆/data/0、/data/1这种没法看的路径等于完全浪费了资源机制。正确做法是按领域实体拆 keyuser、post、comment、order 各自独立路径和语义都清晰。第二id 最好用数字类型。JSON Server 默认以数字 id 自增如果你手写了一些字符串 id 字段前端在做路由跳转时很容易拿出字符串去和后端数字 id 比较出现“明明数据存在但查不到”的怪问题。保持前后端对 id 类型的一致认知能少踩无数坑。第三外键命名一定要和 JSON Server 的推断规则对齐。前面说过父资源单数加 Id比如 userId、postId、commentId。命名对齐后_embed和_expand就不用额外配置开箱即用。如果确实需要不同的字段名你就得自己写中间件或二次处理数据成本明显上升。4. 工程化玩法自定义中间件与假数据生成4.1 从 CLI 走向 server.js自定义路由与中间件CLI 启动方式只能使用 JSON Server 的默认能力一旦碰到默认能力覆盖不了的场景就需要切换到server.js编程模式。编程模式的核心是用json-server包在自己的 Node.js 文件里组装服务。一个最基础的自定义 server.js 长这样const jsonServer require(json-server); const server jsonServer.create(); const router jsonServer.router(db.json); const middlewares jsonServer.defaults(); server.use(middlewares); server.use(jsonServer.bodyParser); server.use(router); server.listen(3000, () { console.log(JSON Server is running on http://localhost:3000); });jsonServer.defaults()包含日志、CORS、静态资源等默认中间件建议保留。jsonServer.bodyParser()用于解析 POST/PUT/PATCH 的 JSON 请求体这是自定义路由里读取参数的必备条件。有了这个基础骨架你就可以在server.use(router)之前插入自定义路由。比如真实接口里有一个登录接口它并不对应 db.json 里的某个资源而是一个独立的处理逻辑。可以这样写server.post(/login, (req, res) { const { username, password } req.body; if (username admin password 123456) { res.json({ token: mock-token-123456, username: admin }); } else { res.status(401).json({ message: 用户名或密码错误 }); } });这样前端请求POST /login时就不会落到 JSON Server 默认的数据库路由上而是被自定义逻辑接管。这能极大扩展 mock 能力让假数据服务更接近真实后端的语义。4.2 用中间件模拟延迟、鉴权与业务校验中间件是 server.js 模式下最值得研究的机制。它本质上是 Express 中间件在请求进入 JSON Server 路由之前做一层拦截和处理。最常见的用法是给所有请求加延迟这在测试前端加载状态和骨架屏时很有价值server.use((req, res, next) { setTimeout(next, 500); });这段代码会给每个请求统一增加 500 毫秒延迟。注意一定要在函数里调用next()否则请求永远卡在中间件里前端页面会一直转圈。如果你想模拟不同接口不同延迟可以对路径做判断比如/slow路径延迟 2 秒其余路径延迟 200 毫秒。模拟鉴权同样用中间件实现。你可以约定前端请求 header 必须携带Authorization: Bearer mock-token-123456中间件检查到没有这个头就返回 401server.use((req, res, next) { if (req.path.startsWith(/admin) !req.headers.authorization) { res.status(401).json({ message: 未登录 }); } else { next(); } });这种做法的价值和 Mock.js 直接拦截请求完全不同。前端代码在前请求真实发出加载组件真实进入错误状态错误提示真实展示整个链路和线上环境几乎没有差别。等到后端就绪你只需要把 baseURL 切走即可。业务校验也可以写在中间件里。比如订单接口后端要求下单时必须传 goodsId 和 quantitymock 环境同样应该校验这些字段。不校验的后果是前端提交空表单也能成功页面逻辑是不是真的正确完全没有被验证。server.post(/orders, (req, res) { const { goodsId, quantity } req.body; if (!goodsId || !quantity) { return res.status(400).json({ message: goodsId 和 quantity 必填 }); } res.json({ id: Date.now(), goodsId, quantity, status: CREATED }); });4.3 用 faker 或 mockjs 批量生成假数据自己手写几十条 mock 数据还能接受但要准备一百条带随机属性的帖子、几十个用户、几百条评论手写就太累了。生成假数据有两个常用方案一个叫 Mock.js一个叫 Faker.js。Mock.js 中文文档全、随机中文内容能力强更贴合国内开发者习惯Faker.js 的英文语料丰富、API 现代被很多国际项目采用。我一般写一个独立的生成脚本比如mock/generate.js用 Faker 生成数据后写入 db.json。下面是一个简单但可用的示例const fs require(fs); const { faker } require(faker-js/faker); const users Array.from({ length: 20 }, (_, i) ({ id: i 1, name: faker.person.fullName(), email: faker.internet.email(), avatar: faker.image.avatar() })); const posts Array.from({ length: 100 }, (_, i) ({ id: i 1, title: faker.lorem.sentence(), userId: Math.ceil(Math.random() * 20), views: faker.number.int({ min: 0, max: 9999 }) })); const db { users, posts }; fs.writeFileSync(db.json, JSON.stringify(db, null, 2));执行node mock/generate.js之后db.json 就自动生成了 20 个用户和 100 篇文章userId 随机关联到已有用户。看到 userId 的生成逻辑了吗这里特意让 userId 落在 1 到 20 范围内就是为了保证_expanduser关联查询能正常工作。设计假数据时外键的取值范围必须与主资源 id 对齐不然会出现大量悬空引用。4.4 前端环境变量切换与接入策略JSON Server 跑起来只是第一步更关键的是前端工程如何平滑接入。我的做法是从一开始就把请求层设计为可切换的而不是临时改代码去适配 mock 地址。以 Vite 项目为例我在项目根目录建.env.development文件VITE_API_BASE_URLhttp://localhost:3000然后封装请求工具import axios from axios; const request axios.create({ baseURL: import.meta.env.DEV ? http://localhost:3000 : import.meta.env.VITE_API_BASE_URL });这样开发环境默认全部走 mock 服务测试环境或生产环境则走真实接口。如果你用的脚手架是 Vue CLI环境变量名就是VUE_APP_API_BASE_URL如果是 webpack可以结合process.env.NODE_ENV做判断。核心思路都一样用一个环境变量控制 baseURL切换 mock 和真实后端时前端业务代码一行都不用改。顺便提醒两个细节。第一本地 mock 服务在开发时如果和前端跨域默认 JSON Server 已经帮你加了 CORS 响应头前端直接请求没有压力。但在 Webpack 或 Vite 的 dev server 场景下我更推荐用代理转发把/api代理到http://localhost:3000这样前端代码里的路径可以统一成/api/posts这种相对路径后续切真实后端时只需改代理目标。第二mock 服务属于开发环境资源记得在工程配置里把它限制在开发阶段常驻不要让它混入构建流程。5. 实际踩坑与排查记录5.1 端口占用与进程残留问题JSON Server 跑久了之后最常见的问题是端口被占用。明明执行了启动命令终端却报Error: listen EADDRINUSE: address already in use :::3000。这通常是有上一个 JSON Server 进程没被关闭或者 IDE 里的调试进程还活着。排查端口占用我一般用两个命令。macOS 和 Linux 下用lsof -i :3000可以看到占用进程的 PID然后kill -9 PID强制结束。Windows 下用netstat -ano | findstr :3000查看 PID再用taskkill /PID PID /F结束进程。如果你经常遇到老进程残留我建议把 JSON Server 的启动命令封装成 npm script例如mock: json-server --watch db.json --port 3000并且在开发完后统一通过终端退出。频繁改代码导致进程不干净时重启开发机比一个个查端口更快。5.2 中文乱码与编码问题JSON Server 对数据文件本身的编码要求不高但 Windows 用户在终端里启动服务或读取 JSON 时可能遇到中文乱码。出现乱码的根源通常是两个终端代码页不支持 UTF-8以及 db.json 文件本身被保存成了非 UTF-8 编码。排查思路按顺序来先确认编辑器保存格式是 UTF-8没有 BOM再在启动 JSON Server 的终端里执行chcp 65001把代码页切到 UTF-8最后确认 JSON Server 返回的响应头里包含charsetutf-8。默认情况下字体应该是正常的因为中间件已经设置了合适的 Content-Type如果自己写了 express 路由返回中文记得显式调用res.json()而不是自己拼字符串这样中文一般不会翻车。5.3 id 类型不一致、顶层结构与跨域问题id 类型不一致是我在关联查询时踩过最深的一个坑。db.json 里 id 是数字 1、2、3前端业务代码里却拿字符串1去拼 URL请求GET /posts/1没问题但有些场景下GET /posts?id1就匹配不到。所以我始终建议mock 数据的 id 类型要和后端约定保持一致并且前端不要对 id 做隐式类型转换。还有一类问题是响应结构格式。很多公司后端喜欢统一包一层结构比如{ code: 0, data: [...] }前端请求拦截器也按这个结构解包。但 JSON Server 默认返回的是裸数组或裸对象不是包一层 code 和 data 的结构。解决方案有两个方向一个是在前端请求拦截器里兼容这种裸响应另一个是写一个中间件在返回前统一包装。我更推荐前者因为前端逻辑不需要知道数据来自 mock 还是真实后端。如果你确实需要模拟后端包装结构可以自己包一个类似res.json({ code: 0, data: responseData })的中间件但要注意 JSON Server 自带的查询参数逻辑会被打乱需要额外处理所以我个人一般不做这层包装。最后是跨域问题。开发时前端跑在 5173 端口JSON Server 跑在 3000 端口两者不同源浏览器会发起 CORS 预检请求。JSON Server 默认开启 CORS对 GET、POST 这些简单请求基本没问题但遇到带自定义 header 的请求时预检可能失败。这时候优先配置前端 dev server 的代理让前端页面访问同源路径再由 dev server 转发到 3000 端口。Vite 配置类似这样server: { proxy: { /api: { target: http://localhost:3000, changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) } } }5.4 JSON Server 的能力边界与替代方案写到最后还是要坦诚地聊一聊 JSON Server 的边界。它在“基础 CRUD 接口 mock”这个领域确实好用但真实业务中总有一些它覆盖不到的地方文件上传、复杂权限模型、登录态持久化、定时任务推送、WebSocket 长连接、联合查询性能等。遇到这些场景也别慌仍有两种应对方式。第一种是在 JSON Server 基础上扩展。服务器实例本质是 Express 应用你有非常大的自由度去补充自定义路由和中间件。文件上传你可以用 multer 或 busboy 实现WebSocket 可以用 socket.io 组合到服务里。这种方式保持了 mock 环境的统一入口不需要额外起第二个服务难度适中。第二种是直接换更专业的生产级 mock 平台比如 MSW、YApi、Apifox 等。MSW 的全称是 Mock Service Worker它在新一代前端 mock 方案里很受欢迎拦截层级更低可以模拟接口返回、网络错误、断网等复杂场景。YApi 和 Apifox 则更适合团队协作。如果你发现 JSON Server 已经满足不了业务复杂度且团队协作越来越频繁尽早换工具比硬着头皮塞逻辑更划算。我个人在实际项目中一般把 JSON Server 定位在“本地开发期的轻量方案”和“小型演示的最佳选择”它不追求生产级能力但能把前端开发链路里最关键的一环补起来。在我目前的团队里新项目启动时我一般会在 README 里写清楚 mock 启动命令和 db.json 的数据维护方式后端就绪后也只是改环境变量的事这中间省下的等待时间是实打实的。如果你还在等接口不妨花两分钟试一下 JSON Server把 mock 服务跑起来你会发现前端开发节奏可以快很多。