ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

json-server实战:零代码实现前端接口模拟与联调加速

json-server实战:零代码实现前端接口模拟与联调加速 第一次听说 json-server 的时候我正被后端接口进度卡得焦头烂额。需求评审完前端排期排得密不透风结果后端同学拍着胸脯说“接口下周给你”结果下周复下周眼看联调时间被压缩得只剩两三天前端组只好在代码里写死一堆 mock 数据写到最后连字段名改没改都记不清了。后来我翻到了 json-server 这个包当天下午就把整个项目的模拟接口全部搭了起来工位旁边的同事还以为后端提前提测了。这个工具说白了就是一句话你给它一个 JSON 文件它就帮你把一个完整的假后端跑起来。零代码、免数据库、免部署一条命令就能监听端口提供一整套满足常规需求的标准 REST API。前端拿到它等于提前把接口联调这关给过了等真后端上线后再把地址一换就行。这几年我不管做内部管理系统、活动页面还是小程序 Demo凡是涉及前后端分离开发的场景都会先起一个 json-server 放在那省下的时间不是一点点。这篇文章我把自己用这套工具积累的配置方法、核心参数、路由规则和踩过的坑全部整理出来新手可以用它快速建立自己的第一个模拟后端老手也能在里面找到一些平时不太注意的细节。1. 为什么需要 json-server 这样的零代码后端模拟工具1.1 前后端分离模式下的联调困境现在的前端开发早就不是“写写页面切切图”的阶段了大部分项目都走前后端分离前端代码独立部署接口数据全靠 HTTP 请求从后端拿。于是出现一个经典问题后端接口的进度永远落后于前端页面的开发进度。需求文档里写的“用户信息列表接口”后端可能要等到数据库表设计完、权限体系搭完、缓存方案定完才开始动手而前端此时已经把页面的 UI 搭得差不多了总不能干等着。传统的解决办法是前端自己写 mock 数据具体操作分两种一种是直接在代码里定义常量数组用完就删另一种是用一些在线 Mock 平台在网页上填字段和规则生成一堆随机数据。第一种的问题在于写死的常量不能模拟 HTTP 请求的完整链路promise、loading、错误码这些交互体验全都要靠脑补第二种则受限于平台的规则表达力一旦涉及分页、排序、跨表关联很快就会碰到表达不出来的情况。我自己经历过的最尴尬的场景是页面用 mock 数据调通了等后端接口真正出来以后一对接发现字段名不同、嵌套层级不同、分页参数格式也不同前端代码几乎要重写一遍。所以后来我养成了一个习惯所有 mock 数据必须模拟真实 HTTP 接口的契约json-server 恰好就是为此而生的——它不是让你在页面里塞静态数据而是真的起一个本地服务拥有真实的路由、真实的请求方式、真实的响应结构。1.2 json-server 的核心原理JSON 文件到 REST API 的映射很多人第一次听到“零代码后端模拟”会觉得有点玄乎其实 json-server 的原理并不复杂。它底层依赖两个核心库express 负责接管 HTTP 请求和响应lowdb 负责通过 JSON 文件实现数据的读取与持久化。运行时json-server 会加载你指定的 JSON 文件把文件的顶层字段解析成一个个“资源”每个资源名就是一个接口集合名。举个例子现在我写一个 db.json{ users: [ { id: 1, name: 张三, age: 28 }, { id: 2, name: 李四, age: 32 } ], posts: [ { id: 1, title: 第一篇文章, userId: 1 } ] }json-server 启动后会自动生成以下接口GET /users获取用户列表GET /users/1获取单个用户POST /users新增用户PUT /users/1整体更新用户PATCH /users/1局部更新用户DELETE /users/1删除用户posts 资源同样有这一整套操作。也就是说你只要维护 JSON 文件里的数据格式剩下的事情工具全包了。这个设计之所以叫“零代码”是因为它完全基于“约定大于配置”的思路——你不写任何后端逻辑资源名就是路由名数据字段决定返回结构HTTP 方法决定操作类型。对前端同学来说这就像一个开着电动车的外卖员你不用关心发动机怎么点火、路线怎么规划只需要把东西打包好JSON 写好它自己就能送到目的地接口返回。2. 5分钟搭好第一个后端模拟服务2.1 环境准备与安装操作之前先确认电脑上装好了 Node.js版本不用太高16 以上基本就够用。怎么确认终端执行node -v看到 v16 或更高的版本号就没问题。接下来全局安装 json-servernpm install -g json-server有的团队会把它装到项目里而不是全局方便其他成员 clone 完仓库后统一安装使用这个看个人习惯。我想提醒的是有些老项目可能还在用 json-server 0.15 或 0.16 的老版本新版本在 2023 年以后已经更新到 0.17 以上命令行的启动方式和默认行为有一些变化我下面讲的内容以 0.17 版本为准。如果你不想全局装也可以在项目根目录下执行npm install json-server --save-dev然后在 package.json 的 scripts 里加一条命令{ scripts: { mock: json-server --watch db.json --port 3000 } }以后只需要 npm run mock 就能启动服务这个做法在多成员协作时特别重要因为大家 npm install 完就能用不用各自去记命令。2.2 db.json 文件的设计规范db.json 是整套 mock 服务的数据库文件也是唯一需要你自己维护的东西。文件顶层要放资源名和对应的数据内容这里有两种写法需要区分开。第一种是顶层字段的值为数组这时候每个元素就是一个独立的记录接口层面对应的语义是“集合”{ articles: [ { id: 1, title: 零代码后端模拟实战, category: 前端 }, { id: 2, title: json-server 指南, category: 工具 } ] }第二种是顶层字段的值为对象通常用于存放全局配置、字典数据、当前用户信息这类不是列表形态的数据{ profile: { name: 管理员, avatar: https://example.com/avatar.png } }这种写法的资源对应接口 GET /profile会直接返回整个对象。值得注意的是数组形式的资源在数据为空时json-server 会自动为 POST 请求生成以 1 为起点的自增 id对象形式的数据没有这层处理它主要服务于读取场景。我个人的建议是资源按数组来写每条记录都要有 id 字段这是工具约定俗称的主键。如果你不给 id后续用 PUT、PATCH 按 id 更新时会找不到目标记录DELETE 也会失效整个资源就只能读取不能写。2.3 启动服务一行命令跑起来文件准备好以后进入 db.json 所在目录执行json-server --watch db.json--watch 参数的作用是监听文件变化你手动修改 db.json 保存后服务会自动加载最新的数据不需要重启。这也是 json-server 使用体验里非常重要的一环你把它当数据库编辑器来用了改完 JSON 立刻刷新页面就能看到新数据这种即时反馈对前端调试太顺手了。启动成功以后终端会显示资源地址列表默认端口是 3000。如果你的 3000 端口已经被别的程序占了用 --port 参数指定一个新端口json-server --watch db.json --port 4000服务启动后在浏览器打开http://localhost:4000/articles就能看到 articles 集合的 JSON 数组。如果你想要带缩进格式的阅读体验可以装一个叫 JSON Viewer 的浏览器扩展浏览器里直接格式化展示接口数据联调的时候比盯着终端舒服很多。2.4 数据修改的直接体验与文件持久化这是 json-server 区别于很多临时 mock 方案的优点它对数据的所有写操作都会保存回 JSON 文件。比如我用 POST 往 /articles 里新增一条记录然后打开 db.json 文件会看到这条记录已经真的写到文件里了。这个特性听起来普通但实际用起来省事得惊人。以前用在线 mock 平台数据保存在对方服务器里一旦服务商挂了或者项目迁移所有 mock 数据全部要重建。而 json-server 的“数据库”就是一个文本文件放进 git 仓库里任何同事 clone 下来都有完整的数据环境联调环境的可复现性直接拉满。3. 常用接口操作从增删改查到复杂查询3.1 RESTful 风格的增删改查json-server 模拟的是标准 REST API所以各种 HTTP 方法对应的语义非常清楚和真实后端的对接成本也非常低。它的核心操作如下HTTP 方法请求路径作用请求体要求GET/articles获取列表无GET/articles/1获取 id 为 1 的单条记录无POST/articles新增一条记录需要 JSON 请求体PUT/articles/1整体替换 id 为 1 的记录请求体需要包含完整字段PATCH/articles/1局部更新 id 为 1 的记录请求体只需写要改的字段DELETE/articles/1删除 id 为 1 的记录无这里想重点聊聊 PUT 和 PATCH 的区别因为很多新手在这里吃过亏。PUT 是整体替换比如我有一条记录是 { id: 1, title: A, category: 前端 }如果我 PUT 请求体只写 { title: B }那这条记录更新后就会变成 { id: 1, title: B }category 字段直接消失。PATCH 是局部更新请求体写 { title: B }保存后原记录只会把 title 改成 Bcategory 依然保留。实际开发中后端的接口实现风格各不相同有的只支持 PUT有的只支持 PATCH所以你在 mock 阶段最好两种都验证一遍免得等到真后端接口出来以后才发现前端代码调用了后端不支持的更新方式。3.2 查询参数过滤、分页、排序、搜索一个都不少json-server 能走到今天这个位置靠的可不仅仅是基础增删改查。它内置的查询参数体系非常接近真实项目的复杂列表需求我把最常用的几个整理出来。过滤功能通过字段名作为查询参数直接传值GET /articles?category前端 GET /articles?age28多个参数之间是 AND 关系GET /articles?category前端author张三范围类过滤用 _gte 和 _lte 表示大于等于和小于等于GET /articles?views_gte100views_lte500全文搜索用 q 参数GET /articles?qjson这个搜索会对当前资源的所有字段做模糊匹配适合快速找数据。分页用 _page 和 _limit 控制默认一页返回 10 条GET /articles?_page1_limit10结果响应头里会带上 X-Total-Count 字段表示总记录数很多真实后端的列表响应也是这个套路前端分页组件可以直接借用这个字段。排序用 _sort 和 _orderGET /articles?_sortviews_orderdesc复杂一点的排序需求也能满足比如按多个字段排序GET /articles?_sortcategory,views_orderasc,desc还有一个很少人注意到但很实用的操作返回部分字段。用 _expand 会包含关联数据这在下文会细讲而如果你只想拿列表里某几个字段可以用查询参数配合 JSON 的字段筛选。对于隐藏敏感字段、减少响应体积的场景这个操作在调试时很高效。我把这些查询参数的格式和用途整理成一张表方便对照使用参数示例作用字段名?category前端等值过滤_gte / _lte?views_gte100数值范围过滤_ne?category_ne旧闻排除特定值_like?title_like指南模糊匹配单个字段q?q后端全字段模糊搜索_page / _limit?_page2_limit10分页_sort / _order?_sortid_orderdesc排序嵌套字段?author.name张三对嵌套对象字段过滤这些参数完全可以组合使用例如GET /articles?category前端_sortviews_orderdesc_page1_limit10表示取出所有前端分类的文章按浏览量从高到低排序并返回第一页的十条数据。这种表达能力已经覆盖了绝大多数业务列表接口的需求拿来做前端联调绰绰有余。3.3 关系数据的模拟_embed 和 _expand真实项目里的资源之间常有外键关系比如文章属于某个作者、订单包含多条商品明细、部门下面有一堆员工。json-server 也考虑到了关系数据的模拟提供了两个很关键的参数_embed 和 _expand。_embed 的作用是把子资源内嵌到父资源中返回。比如 db.json 里有两个资源{ departments: [ { id: 1, name: 技术部 }, { id: 2, name: 产品部 } ], employees: [ { id: 1, name: 张三, departmentId: 1 }, { id: 2, name: 李四, departmentId: 1 }, { id: 3, name: 王五, departmentId: 2 } ] }请求GET /departments?_embedemployees返回结果就会变成[ { id: 1, name: 技术部, employees: [ { id: 1, name: 张三, departmentId: 1 }, { id: 2, name: 李四, departmentId: 1 } ] } ]_embed 的关键点在于子资源中必须存在一个字段名符合父资源名Id规律的外键employee 里的 departmentId工具才能正确关联。_expand 则反过来当你把父资源作为内嵌对象塞进子资源时使用GET /employees?_expanddepartment返回结果会变成[ { id: 1, name: 张三, departmentId: 1, department: { id: 1, name: 技术部 } } ]很多前端同学刚开始分不清这两个参数我在这里给你一个简单的记忆方式_embed 是把子级塞进父级_expand 是把父级粘到子级上。它们在真实项目里对应的场景非常常见比如用户列表带出部门信息、订单列表带出商品详情。mock 阶段把这种嵌套关系模拟出来后续接真后端时的数据形态转换成本就小很多。4. 进阶玩法把 json-server 调教成真正可用的 mock 服务4.1 自定义路由接口路径自己说了算默认情况下db.json 里 resource 叫什么名接口路径就是什么。但真实后端的接口路径往往有一套自己的命名规范比如前端需要请求 /api/articles而不是直接 /articles。这时候就需要自定义路由映射。json-server 支持通过一个额外的 JSON 文件实现路由重写。先创建 routes.json{ /api/*: /$1, /articles/list: /articles, /article/detail/:id: /articles/:id }启动命令变成json-server db.json --routes routes.json注意使用路由重写后--watch 参数仍然可以加不影响数据监听。这里第一行 “/api/*”: “/$1” 是很规整的写法意思是凡是以 /api 开头的请求都会把 /api 部分去掉后传给 json-server 内部处理也就是前端访问 /api/articles实际命中的还是 /articles 资源。这种自定义路由能力解决了一个很实际的联调问题很多公司的后端接口地址统一带项目名前缀比如 /api/order/list、/api/user/info。前端如果一开始就用完整假地址联调等切到真后端后所有的 axios 请求地址、代理规则可能都要调整。而用 json-server 把路径重写做好前端代码就能完全按照真实环境的 URL 规范来写后续切换只需要改一行 baseURL 配置。4.2 用中间件模拟网络延迟、鉴权和日志json-server 虽然零代码上手但它毕竟是基于 express 构建的所以也能编写中间件来扩展能力。这个扩展点平时容易被忽略却相当实用。最常见的需求是模拟网络延迟。真实开发中接口响应不可能都是毫秒级的前端要处理 loading 状态还要规避重复提交这些交互逻辑需要建立在有延迟的接口环境下才能充分测试。创建一个 delay.js 文件module.exports (req, res, next) { setTimeout(next, 1500); };然后启动时加参数json-server db.json --middlewares delay.js这样所有请求都会延迟 1.5 秒返回前端 loading 和防重复提交的交互就能得到充分验证。更进一步你还可以用中间件做简易的接口鉴权模拟。比如创建一个 auth.jsmodule.exports (req, res, next) { if (req.path ! /login !req.headers.authorization) { res.status(401).json({ message: 未登录 }); return; } next(); };这样除了登录接口外其他接口在没有携带 Authorization 请求头时都会返回 401。前端在 mock 阶段就能把登录态管理、token 拦截、断网提示这些逻辑验证完联调时的交付质量会有明显提升。顺带提一句json-server 还支持在数据文件中配置 --config 文件的启动项比如设置静态资源目录、自定义响应头等。多个工程需要统一 mock 方案时把这些配置抽取成一个公共配置文件整套环境就能复制到任何项目里。4.3 用 JS 脚本启动一步到位集成到项目如果你只在命令行敲启动命令项目的 mock 环境依赖着每个开发者的记忆时间久了总会有人忘记某个参数。更好的做法是写一个启动脚本把端口、路由、中间件全部固化下来。创建 server.jsconst 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((req, res, next) { res.header(Access-Control-Allow-Origin, *); res.header(Access-Control-Allow-Methods, GET, POST, PUT, PATCH, DELETE); res.header(Access-Control-Allow-Headers, Content-Type, Authorization); next(); }); server.use(router); server.listen(4000, () { console.log(模拟后端已启动: http://localhost:4000); });再把 package.json 里的 scripts 改成{ scripts: { mock: node server.js } }以后不管谁来接手这个项目只需要 npm install npm run mock 就能启动完整可用的模拟后端。这一步从长远来看非常值得团队协作时它消除了环境配置上的沟通成本。代码里甚至可以加上一些模拟数据的初始化逻辑比如根据日期动态生成不同数据或者批量生成几百条随机用户记录让联调数据更接近真实体量。4.4 高级查询排序、切片与复杂过滤实战前面讲了查询参数的常用部分这里补充一些不常为人提但关键时刻很有用的高级查询手段。切片操作用 _start 和 _end它们和 _page 的区别在于分页是“页”的粒度切片是“位置”的粒度GET /articles?_start0_end5这条请求会返回从第 0 条到第 5 条不含第 5 条的记录。在做跨页勾选、无限滚动这类功能时切片比传统的页数控制更灵活。全文搜索 q 参数可以和过滤参数同时使用GET /articles?category前端qjson-server这在模拟后台管理系统的搜索场景时非常自然。还支持嵌套对象的字段过滤比如员工资源里每人都带一个 address 对象字段是 city你可以直接这样过滤GET /employees?address.city北京点号语法在 json-server 的过滤解析中是被支持的知道这个细节的人不多而它在真实联调时又能精准匹配到后端接口的查询逻辑。总之把这些高级查询操作组合起来mock 环境几乎能覆盖你项目中任何复杂列表需求。5. 排坑实录那些年 json-server 踩过的坑5.1 POST 数据不自动生成 id检查你的资源格式很多人第一次用 json-server 时会发现往列表 POST 一条数据后返回的记录里没有 id 字段。这不是工具的 bug大概率是 db.json 里该资源没有使用数组格式或者数组为空时工具无法推断 id 的起始值。我实测下来数组格式的资源配置为{ users: [] }POST 后生成的数据会是 id: 1。但如果资源被配置成了对象格式{ users: {} }POST 操作虽然能写入但接口语义就会变得混乱后续按 id 查找更是无从谈起。所以动手之前先确认资源的底层数据结构这决定了所有写操作的可用性。5.2 _embed 和 _expand 的边界数据量一大就翻车_embed 功能在数据量小的时候很好用但在数据量变大以后很容易出现问题。我举一个真实的例子模拟一个订单系统一个订单关联十来个商品明细请求 GET /orders?_embeditems每个订单都会把 items 全量塞进来100 条订单数据一次性返回响应体轻松超过 800KB浏览器直接卡顿。对此我的建议是mock 阶段不要过度依赖 _embed更稳妥的方式是在资源层面把关联数据直接设计成嵌套结构比如订单资源本身就带 items 数组或者只对单条数据用 _embed列表页尽量用多个独立接口来做“前端数据组合”。前端拿到列表后用二次请求拼装数据虽然代码量多一点但这和真后端接口的读取方式是一致的前后端协作思路。5.3 端口占用、重启失败、中文路径乱码端口占用是最常见的问题。启动时报错提示 EADDRINUSE直接换个端口就行json-server db.json --port 5000另一个很早踩过的坑是项目路径里带空格和中文导致工具找不到 db.json。这不是 json-server 独有的问题Node.js 在部分环境下对中文路径的处理确实容易出幺蛾子。我的建议是项目目录统一使用英文避免在“我的文档/项目/xx”这种路径下直接跑命令行工具。5.4 不要把 json-server 当正式后端用这是最需要提醒的一点json-server 只是一个开发辅助工具它不适合作为生产环境的后端服务也无法应对真实产品环境的并发和高可用需求。它的数据存储是单文件的没有数据库的索引机制也没有事务、权限体系、审计日志更没有多实例部署的横向扩展能力。我见过有同学图省事把一个内部工具的“正式接口”直接用 json-server 维护在服务器上前几个月还好后面数据量大了一打开接口就要等好几秒而且写操作并发一上来JSON 文件偶尔出现内容错乱。所以该上正经后端的时候别犹豫json-server 的意义在于把开发流程里的等待时间省掉而不是替代后端工程本身。5.5 调试技巧把终端信息变成排查利器最后分享一个调试习惯。json-server 启动后自带一个简版日志输出但只有请求方法、路径和状态码。如果你觉得信息不够可以自己写一个日志中间件module.exports (req, res, next) { console.log(${new Date().toISOString()} ${req.method} ${req.url}); const originalSend res.json; res.json function (data) { console.log(响应:, JSON.stringify(data).slice(0, 300)); originalSend.call(this, data); }; next(); };把这个中间件挂在启动参数里每个请求对应的参数和响应内容都会输出到终端。联调时遇到“前端传的参数对不对”“返回为什么和预期不一致”这类问题基本不用再打开浏览器 DevTools 反复确认看终端信息就能把问题定位在请求层还是数据处理层。我个人在实际项目里已经把 json-server 用成一个标准流程每个前端工程仓库里放一个 mock 目录里面有 db.json、routes.json、server.js 和几个中间件团队所有成员 npm run mock 就能拥有一模一样的开发环境。这样做的价值不光在节省联调等待时间更重要的是它让前端在接口契约这件事上掌握更大的主动权字段怎么设计、嵌套怎么组织、分页怎么约定前端同学可以先按理想方式排一版再跟后端对齐沟通效率反而提高了不少。类
返回列表