
1. 为什么前端开发离不开数据模拟工具做过前端的人都有一个共同的痛页面写完了接口还没好。后端同事说“再等等这周排期满了”产品经理说“明天先给我看看效果”你夹在中间要么干等要么自己造数据。早期我的做法很原始——在代码里写死一个数组或者用setTimeout模拟一下延迟返回一段静态 JSON。这种方式在页面少、字段少的时候还能凑合一旦接口数量上来了字段结构复杂了增删改查都要模拟代码里就会堆满各种临时变量和注释最后自己都分不清哪些是真实逻辑、哪些是临时补丁。后来我开始找更系统的方案。试过在本地起一个简单的 Node 服务手写路由返回 JSON 文件也试过用一些在线的接口模拟平台但要么需要联网、要么有调用次数限制、要么配置起来特别繁琐。直到用上json-server才算真正找到了一个平衡点它足够轻一条命令就能跑起来它足够真支持完整的 RESTful 接口语义它足够灵活数据用 JSON 文件管理改起来毫无负担。这篇文章我就围绕这个工具把从零搭建、数据建模、进阶技巧到踩坑经验完整梳理一遍不管你是刚接触前端的新手还是想优化本地开发流程的老手都能直接拿去用。2. json-server 到底是什么能解决哪些实际问题2.1 一句话理解它的定位json-server 是一个基于 Node.js 的命令行工具你给它一个 JSON 文件它就能在本地启动一个完整的 RESTful API 服务。这个 JSON 文件里的每一个顶层字段都会被自动映射成一组接口。比如你有一个db.json里面有个users数组那么启动后你就自动拥有了GET /users、GET /users/1、POST /users、PUT /users/1、PATCH /users/1、DELETE /users/1这一整套接口。不需要写一行服务端代码不需要配置路由表不需要定义 Controller。我习惯把它类比成“前端开发者的临时数据库加接口服务器”。它不像真正的数据库那样需要安装、建表、写 SQL也不像 Mock 平台那样需要登录、创建项目、配置字段。它就是“一个文件换一套接口”简单直接。2.2 它解决的核心痛点在没有 json-server 之前前端对接接口通常面临几个典型问题。第一是接口不可用后端还没写完前端只能等或者自己造。第二是数据结构不稳定后端字段改来改去前端跟着反复调整。第三是联调环境复杂有时候测试环境挂了整个团队都停摆。第四是增删改查难以模拟静态 JSON 只能读不能写表单提交、删除操作没法验证。json-server 针对性地解决了这些问题。它让前端在接口定义阶段就能独立开发数据结构由前端自己掌控增删改查全部支持且数据会持久化到 JSON 文件里。更重要的是它启动成本极低一条npx json-server --watch db.json就能跑起来不需要额外安装数据库或配置环境变量。2.3 适合哪些人用这个工具几乎覆盖了所有需要跟接口打交道的前端场景。个人开发者做 side project 时没有后端资源用它最合适。团队开发中前后端并行时前端可以先用它模拟接口等后端好了再切换 baseURL。教学场景下老师讲解 Ajax、Fetch、Axios 时用它提供稳定的数据源。甚至测试同学也可以用它在本地复现特定数据状态验证边界情况。注意json-server 定位是开发阶段的模拟工具不适合直接用于生产环境。它的性能、安全性、并发能力都没有经过生产级验证这一点后面会详细说。3. 从零搭建环境准备与第一个模拟接口3.1 安装方式的选择与理由json-server 的安装有两种主流方式全局安装和项目内安装。全局安装用npm install -g json-server好处是任何目录下都能直接敲json-server命令适合快速试验。项目内安装用npm install json-server --save-dev好处是版本锁定在项目里团队成员拉下代码后npm install就能得到一致的版本不会出现“你那边能跑我这边报错”的情况。我个人的建议是如果是正式项目一律用项目内安装然后在package.json的scripts里加一条命令比如mock: json-server --watch mock/db.json --port 3001。这样团队里任何人执行npm run mock就能启动不需要额外文档说明。如果是临时想试一下用npx json-server最省事不污染全局环境。3.2 最小可用示例三分钟跑起来先创建一个db.json文件内容如下{ users: [ { id: 1, name: 张三, email: zhangsanexample.com, role: admin }, { id: 2, name: 李四, email: lisiexample.com, role: editor } ], posts: [ { id: 1, title: 第一篇帖子, authorId: 1, published: true }, { id: 2, title: 第二篇帖子, authorId: 2, published: false } ] }然后在终端执行npx json-server --watch db.json --port 3001你会看到终端输出类似这样的信息\{^_^}/ hi! Loading db.json Done Resources http://localhost:3001/users http://localhost:3001/posts Home http://localhost:3001这时候打开浏览器访问http://localhost:3001/users就能看到完整的用户列表 JSON。访问http://localhost:3001/users/1得到单个用户。用 Postman 或 curl 发一个 POST 请求到/users传入 JSON body就会新增一条记录并且这个记录会被写回db.json文件下次启动依然存在。3.3 目录结构建议随着模拟数据变多把所有内容塞在一个db.json里会变得难以维护。我通常会在项目根目录建一个mock文件夹里面这样组织mock/ db.json # 主数据文件 routes.json # 自定义路由映射 middlewares.js # 自定义中间件 seed/ # 初始数据种子 users.json posts.json然后在db.json里通过引用或手动合并的方式组织数据。json-server 本身不直接支持文件拆分但可以通过--watch配合脚本在启动前合并或者用js文件作为数据源后面会讲。这种结构在项目变大后优势明显每个模块的数据独立管理改用户数据不会误碰帖子数据。4. 核心功能深度拆解RESTful 语义与数据操作4.1 自动生成的路由规则json-server 最核心的能力就是根据 JSON 顶层字段自动生成路由。假设db.json里有users、posts、comments三个数组那么自动生成的接口包括操作方法路径说明列表GET/users返回全部用户详情GET/users/:id返回指定 id 的用户新增POST/users创建新用户id 自动生成全量更新PUT/users/:id替换指定用户全部字段局部更新PATCH/users/:id只更新传入的字段删除DELETE/users/:id删除指定用户这套规则完全遵循 RESTful 规范前端用 Axios 或 Fetch 调用时跟调用真实后端接口的写法一模一样。这意味着你后期切换真实接口时只需要改baseURL业务代码几乎不用动。4.2 查询参数过滤、排序、分页、全文搜索json-server 支持丰富的查询参数这是它比手写静态 JSON 强大得多的地方。我整理了一张常用参数表参数示例效果字段过滤/users?roleadmin返回 role 为 admin 的用户模糊匹配/users?name_like张name 包含“张”的用户范围查询/posts?id_gte2id_lte5id 在 2 到 5 之间排序/users?_sortname_orderasc按 name 升序分页/users?_page1_limit10第一页每页 10 条全文搜索/users?q张所有字段中包含“张”的记录关联查询/posts?authorId1查询 authorId 为 1 的帖子这些参数可以组合使用比如/posts?_sorttitle_orderdesc_page2_limit5表示按标题降序、取第二页每页五条。分页时响应头里还会带上X-Total-Count方便前端计算总页数。实操心得_like是大小写不敏感的模糊匹配但只对字符串字段生效。如果你需要对数字字段做模糊匹配得先转成字符串或者用_gte/_lte做范围查询。4.3 关联数据与嵌套资源json-server 支持通过_embed和_expand做简单的关联查询。假设posts里有authorId指向users的id那么GET /posts?_embedcomments会把每条帖子关联的 comments 嵌套进去GET /comments?_expandpost会把每条评论关联的 post 展开这种关联是“约定优于配置”的json-server 会自动识别xxxId这样的外键字段。比如comments里有postId那么_embedcomments就能在 post 里嵌入对应的评论列表。这个功能在模拟“文章详情页带评论”这类场景时特别有用不需要手动拼数据。4.4 数据持久化机制json-server 默认会把所有写操作POST、PUT、PATCH、DELETE的结果写回db.json文件。这意味着你通过接口新增的数据重启服务后依然存在。这个特性有利有弊好处是模拟状态可以累积比如你测试分页时可以先造 100 条数据坏处是测试过程中产生的脏数据会污染初始数据下次演示时可能看到一堆乱七八糟的记录。我的做法是把干净的初始数据放在seed/db.json启动前用脚本复制到mock/db.json这样每次启动都是干净状态。或者用--watch监听一个只读的种子文件写操作写到内存里不落盘需要自定义配置。后面进阶部分会给出具体方案。5. 进阶配置让模拟服务更贴近真实后端5.1 自定义端口与主机默认端口是 3000但前端项目通常也用 3000容易冲突。用--port指定其他端口比如--port 3001。如果想让局域网内其他设备访问比如手机真机调试加--host 0.0.0.0然后通过本机 IP 访问。这个在移动端调试时非常实用我经常用手机连同一个 WiFi 直接访问电脑上的模拟接口。5.2 自定义路由映射真实后端的接口路径往往带前缀比如/api/v1/users。json-server 支持通过--routes指定一个路由配置文件{ /api/*: /$1, /v1/*: /$1 }这样访问/api/users就会被映射到/users。前端代码里写/api/users后期切换真实接口时不用改路径。这个配置在团队约定接口前缀时特别有用。5.3 自定义中间件模拟延迟、鉴权、错误码json-server 支持通过--middlewares加载自定义中间件。中间件就是一个函数接收req、res、next三个参数。我常用的几个场景模拟网络延迟module.exports (req, res, next) { setTimeout(next, 800); };模拟鉴权失败module.exports (req, res, next) { if (req.path /users req.method POST) { if (!req.headers.authorization) { return res.status(401).json({ error: 未授权 }); } } next(); };模拟特定错误码module.exports (req, res, next) { if (req.path /error-test) { return res.status(500).json({ error: 服务器内部错误 }); } next(); };这些中间件让模拟服务不再是“永远成功”的理想状态前端可以提前处理加载中、超时、鉴权失败、服务端错误等各种边界情况。这一点在实际开发中价值极高很多 bug 都是在异常流程里暴露的。5.4 用 JavaScript 文件作为数据源除了 JSON 文件json-server 还支持.js文件作为数据源。这意味着你可以用代码动态生成数据module.exports () { const data { users: [], posts: [] }; for (let i 1; i 100; i) { data.users.push({ id: i, name: 用户${i}, email: user${i}example.com, role: i % 3 0 ? admin : editor }); } return data; };启动时用json-server --watch db.js。这种方式适合需要批量造数据的场景比如测试分页、测试列表渲染性能。我试过生成 1000 条用户数据页面滚动加载的表现一目了然。6. 实操全流程一个完整的模拟项目搭建记录6.1 项目初始化与依赖安装假设我们有一个前端项目需要模拟用户管理和文章管理两个模块。首先在项目根目录执行npm init -y npm install json-server --save-dev然后在package.json里添加脚本{ scripts: { mock: json-server --watch mock/db.json --port 3001 --routes mock/routes.json --middlewares mock/middlewares.js } }6.2 数据建模与种子数据准备创建mock/db.json设计用户和文章的数据结构{ users: [ { id: 1, name: 张三, email: zhangsanexample.com, role: admin, createdAt: 2024-01-15 }, { id: 2, name: 李四, email: lisiexample.com, role: editor, createdAt: 2024-02-20 }, { id: 3, name: 王五, email: wangwuexample.com, role: viewer, createdAt: 2024-03-10 } ], posts: [ { id: 1, title: 前端工程化实践, authorId: 1, status: published, views: 1200 }, { id: 2, title: CSS 布局技巧, authorId: 2, status: draft, views: 0 }, { id: 3, title: JavaScript 异步编程, authorId: 1, status: published, views: 850 } ], comments: [ { id: 1, postId: 1, content: 写得很实用, userId: 2 }, { id: 2, postId: 1, content: 期待后续, userId: 3 }, { id: 3, postId: 3, content: 讲得很清楚, userId: 2 } ] }这里我特意设计了几个关联关系posts.authorId指向users.idcomments.postId指向posts.idcomments.userId指向users.id。这样后面可以用_embed和_expand做关联查询。6.3 路由与中间件配置创建mock/routes.json{ /api/*: /$1 }创建mock/middlewares.jsmodule.exports (req, res, next) { // 模拟 500ms 网络延迟 setTimeout(() { // 模拟鉴权非 GET 请求需要 token if (req.method ! GET !req.headers[x-token]) { return res.status(401).json({ code: 401, message: 缺少鉴权令牌 }); } next(); }, 500); };6.4 启动与验证执行npm run mock终端显示服务已启动。然后用 curl 验证几个关键接口# 获取用户列表 curl http://localhost:3001/api/users # 获取文章并嵌入评论 curl http://localhost:3001/api/posts?_embedcomments # 获取评论并展开文章 curl http://localhost:3001/api/comments?_expandpost # 新增用户带鉴权头 curl -X POST http://localhost:3001/api/users \ -H Content-Type: application/json \ -H x-token: test \ -d {name:赵六,email:zhaoliuexample.com,role:editor} # 不带鉴权头应该返回 401 curl -X POST http://localhost:3001/api/users \ -H Content-Type: application/json \ -d {name:测试}实测下来所有接口都按预期工作。新增的用户会写回db.json刷新列表能看到。401 鉴权也正常触发。这套配置已经足够支撑一个中型前端项目的模拟需求。6.5 前端代码对接示例以 Axios 为例前端封装一个请求模块import axios from axios; const api axios.create({ baseURL: http://localhost:3001/api, timeout: 5000, headers: { Content-Type: application/json, x-token: dev-token } }); export const getUsers (params) api.get(/users, { params }); export const getUser (id) api.get(/users/${id}); export const createUser (data) api.post(/users, data); export const updateUser (id, data) api.patch(/users/${id}, data); export const deleteUser (id) api.delete(/users/${id}); export const getPostsWithComments () api.get(/posts?_embedcomments);后期切换真实后端时只需要把baseURL改成真实地址其他代码不动。这就是 json-server 最大的价值让前端开发与后端解耦同时保持接口调用方式的一致性。7. 常见问题与排查技巧实录7.1 端口被占用怎么办启动时报EADDRINUSE说明端口被其他程序占了。最简单的办法是换端口--port 3002。如果想知道谁占了端口Windows 下用netstat -ano | findstr :3000Mac 或 Linux 下用lsof -i :3000。我通常会在项目里固定用 3001 到 3010 之间的端口避开前端常用的 3000 和 8080。7.2 数据写入后没有持久化json-server 默认会持久化但如果你用的是.js数据源写操作不会落盘因为 JS 文件是动态生成的。另外如果db.json文件权限不足写入也会失败。检查文件是否可写以及启动命令是否带了--watch。不带--watch时json-server 只读不写。7.3 关联查询不生效_embed和_expand依赖外键命名约定。如果posts里的外键叫author_id而不是authorId关联就会失败。json-server 识别的是xxxId这种驼峰命名。如果你的数据字段是下划线风格需要手动转换或者在中间件里做映射。7.4 分页总数不对分页时响应头X-Total-Count返回的是过滤后的总数不是全量总数。如果你先过滤再分页这个值是准确的。但如果你需要全量总数得单独发一个不带分页参数的请求。另外跨域时浏览器默认拿不到这个响应头需要在服务端配置Access-Control-Expose-Headersjson-server 默认已经暴露了常用头一般不需要额外配置。7.5 中文搜索乱码URL 里的中文需要编码用encodeURIComponent处理。比如搜索“张三”实际请求路径应该是/users?name_like%E5%BC%A0%E4%B8%89。Axios 会自动处理参数编码手动拼 URL 时要注意。7.6 常见问题速查表问题现象可能原因解决方法启动报端口占用端口被其他程序使用换端口或结束占用进程写操作不生效未加 --watch 或文件只读加 --watch检查文件权限关联查询为空外键命名不符合约定改为 xxxId 驼峰命名分页总数不对过滤条件影响总数单独请求全量数据计数中文搜索无结果URL 未编码使用 encodeURIComponent跨域请求失败浏览器同源策略json-server 默认支持 CORS检查请求头数据被污染测试写入脏数据启动前从种子文件复制避坑技巧我习惯在mock目录下放一个reset.js脚本每次启动前执行node mock/reset.js把种子数据复制到db.json。这样无论上次测试写了多少脏数据下次启动都是干净的。脚本内容很简单就是fs.copyFileSync两行代码。8. 工具选型对比与适用边界8.1 json-server 与其他方案的对比市面上模拟数据的方案不少我整理了一个对比表方便你根据场景选择方案启动成本增删改查数据持久化关联查询适用场景静态 JSON 文件极低不支持不涉及不支持纯展示页面代码内硬编码低手动模拟不涉及手动拼临时演示json-server低完整支持支持支持前后端并行开发在线 Mock 平台中支持平台管理部分支持团队协作、需要分享自建 Node 服务高完整支持需自行实现需自行实现复杂业务逻辑模拟json-server 的优势在于“轻量但完整”。它不像在线平台那样需要注册登录、配置项目也不像自建服务那样需要写大量代码。它就是一个文件加一条命令但提供了足够真实的接口语义。8.2 什么时候不该用 json-serverjson-server 不是万能的。如果你的模拟需求涉及复杂的业务逻辑比如“下单后扣库存、生成订单号、发送通知”这种多步骤流程json-server 的默认能力就不够了需要写大量中间件反而不如直接写一个 Express 服务。另外如果需要模拟 WebSocket、GraphQL、文件上传等非 RESTful 协议json-server 也不支持。还有就是团队协作场景如果多人需要共享同一套模拟数据在线平台可能更方便因为 json-server 的数据文件需要手动同步。8.3 与前端框架的配合建议在 Vue 或 React 项目里我通常会把 json-server 的启动命令集成到开发脚本里。比如用concurrently同时启动前端和模拟服务{ scripts: { dev: concurrently \npm run mock\ \npm run serve\, mock: json-server --watch mock/db.json --port 3001, serve: vue-cli-service serve } }这样开发者只需要执行npm run dev前端和模拟接口一起起来体验非常顺畅。切换真实接口时通过环境变量控制baseURL比如.env.development里写VUE_APP_API_BASEhttp://localhost:3001/api生产环境改成真实地址。9. 我在实际使用中积累的几个小技巧第一个技巧是关于数据备份的。json-server 的写操作会直接修改db.json有时候测试过程中不小心删了重要数据恢复起来很麻烦。我的做法是把db.json纳入 Git 版本管理每次启动前如果发现数据不对直接git checkout mock/db.json就恢复了。同时把db.json加到.gitignore的例外里确保它被跟踪。第二个技巧是关于 ID 生成的。json-server 默认用自增数字作为新记录的 ID但如果你的数据里已经有 ID 了新增时会自动取最大值加一。这个行为在大多数情况下没问题但如果你手动指定了字符串 ID可能会冲突。建议统一用数字 ID或者用 UUID 作为主键并在中间件里处理。第三个技巧是关于性能的。当数据量超过几千条时json-server 的查询会变慢因为它是全量加载到内存再过滤。如果只是测试分页不需要真的造几千条数据用_page和_limit配合少量数据就能验证逻辑。真需要大数据量测试建议用.js数据源动态生成而不是写死在 JSON 文件里。第四个技巧是关于调试的。json-server 启动后访问根路径http://localhost:3001会看到一个简单的首页列出了所有可用资源。这个页面在快速确认服务是否正常时很有用。另外终端里会实时打印每个请求的方法和路径排查问题时一眼就能看到前端到底发了什么请求。这个工具我用了好几年从个人项目到团队协作都离不开它。它不复杂但足够解决前端开发中最常见的“等接口”问题。如果你还没用过建议今天就花十分钟试一下相信你会跟我一样把它加入日常开发工具箱。