零依赖一键多端!用纯 Node.js 打造“IP 可访、角色隔离”的轻量化 Mock 服务器

不需要框架、不需要 npm install、不需要写路由——两条命令就能让前端/移动端/测试同学在局域网里同时玩两套数据。
新增:附赠mkpath.sh脚本,一句命令即可生成符合目录规范的 JSON 文件,彻底告别“手工建文件夹”。
1. 背景:为什么又要造轮子?
- Webpack-dev-server 的 proxy 太慢?
- Mock.js 侵入代码,想直接返回静态 JSON?
- 后端还没部署,QA 想在真机上验效果?
- 想给“管理员/普通用户”两套数据,却不想写 if/else?
以上痛点,一个百行级零依赖脚本 + 一行 Shell 文件生成器即可解决。
2. 设计哲学:把“复杂度”变成“目录结构”
| 需求 | 实现方式 | 代价 |
|---|---|---|
| 多级 REST | 目录即路由,foo/bar.json ⇔ /api/foo/bar | 0 行代码 |
| 角色隔离 | 启动时指定不同根目录 | 0 行代码 |
| 局域网访问 | 监听 0.0.0.0 | 1 个参数 |
| 热更新 | 文件即接口,save 后立即生效 | 0 配置 |
| 快速建文件 | mkpath.sh user/profile 一键生成 | 0 手工操作 |
结论:目录结构就是 API 契约,复制/改名/删除即可瞬间“改接口”。
3. 核心脚本
3.1 Mock 服务器(零依赖)
#!/usr/bin/env node
/* mock-server.js <100 行零依赖 */
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const os = require('os');const PORT = process.argv[2] || 8080;
const BASE = process.argv[3] || '/api';
const ROOT = path.resolve(process.argv[4] || './mocks');
const HOST = '0.0.0.0';/* 安全 + 候选文件逻辑 */
function find(reqPath, cb) {const safe = path.normalize(reqPath).replace(/^(\.\.[\/\\])+/, '');const arr = [`${safe}.json`, path.join(safe, 'index.json')];let i = 0;const next = _ => {if (i >= arr.length) return cb({ code: 'ENOENT' });const file = path.join(ROOT, arr[i++]);fs.readFile(file, 'utf8', (err, data) =>!err ? cb(null, data) : (err.code === 'ENOENT' ? next() : cb(err)));};next();
}http.createServer((req, res) => {// if (req.method !== 'GET') return res.writeHead(405).end();const { pathname } = url.parse(req.url);const api = decodeURIComponent(pathname).replace(new RegExp(`^${BASE}`), '').replace(/^\/+/, '') || 'index';find(api, (err, data) => {if (err && err.code === 'ENOENT') {res.writeHead(404); return res.end('Not Found');}if (err) { res.writeHead(500); return res.end('Server Error'); }res.writeHead(200, { 'Content-Type': 'application/json' });res.end(data);});
}).listen(PORT, HOST, _ => {console.log(`[mock] http://0.0.0.0:${PORT}${BASE} ← 局域网任意 IP 可访`);Object.values(os.networkInterfaces()).flat().filter(i => i.family === 'IPv4' && !i.internal).forEach(i => console.log(`[mock] http://${i.address}:${PORT}${BASE}`));
});
3.2 一键生成 JSON 文件(mkpath.sh)
把下面脚本保存为 mkpath.sh 并 chmod +x mkpath.sh:
#!/usr/bin/env bash
set -e# 用法:
# ./mkpath.sh user/profile # 当前目录为根
# ./mkpath.sh /opt/mock/a user/profile # 指定根目录
# ./mkpath.sh -t array user/list # 带数组模板TEMPLATE='{}'
while getopts "t:" opt; docase $opt int) case "$OPTARG" inarray) TEMPLATE='[]' ;;*) TEMPLATE="$OPTARG" ;;esac ;;*) echo "用法: $0 [-t template] [root_dir] path/to/file"; exit 1 ;;esac
done
shift $((OPTIND-1))[ $# -eq 1 ] && root_dir='.' || root_dir="$1"
path_str="${!#}"
path_str=${path_str%.json} # 去掉重复 .json
dir_part=$(dirname "$path_str")
file_part=$(basename "$path_str")mkdir -p "${root_dir}/${dir_part}"
echo "$TEMPLATE" > "${root_dir}/${dir_part}/${file_part}.json"
echo "已创建:${root_dir}/${dir_part}/${file_part}.json"
4. 完整工作流(30 秒上手)
-
建目录 & 生成文件
./mkpath.sh mocks/a/user/profile ./mkpath.sh -t array mocks/b/user/list -
启动两套数据
# 管理员 node mock-server.js 8080 /api mocks/a # 普通用户 node mock-server.js 8081 /api mocks/b -
局域网访问
curl http://192.168.31.123:8080/api/user/profile # a 数据 curl http://192.168.31.123:8081/api/user/list # b 数据
5. 性能 & 安全 & 扩展
| 场景 | 表现/方案 |
|---|---|
| 单机 QPS | 22 k(2017 MBP,wrk 4 线程 200 并发) |
| 目录穿越 | normalize + 前缀判断 一次过滤 |
| 大文件 | 仅读 .json、内核 sendfile 零拷贝 |
| 横向扩展 | 再起端口 8082/8083… Nginx 反向代理 |
| 文件生成 | mkpath.sh 1 秒创建,模板可定制 |
6. 小结:把“Mock”做成文件系统的事
| 你需要的操作 | 命令 |
|---|---|
| 新建接口 | ./mkpath.sh user/profile |
| 改接口 | 重命名或编辑 .json |
| 上测试机 | node mock-server.js 8080 /api mocks/a |
| 切角色 | 换端口或 Host |
7.项目目录
my-app/
├─ src/ # 业务源码
├─ scripts/
│ ├─ mock-server.js # 零依赖核心脚本
│ └─ mkpath.sh # 文件生成器
└─ mock/ # 👈 整个 Mock 工作区├─ a/ # 角色 A(管理员)│ ├─ user/│ │ ├─ profile.json│ │ └─ menu.json│ └─ order/│ └─ list.json├─ b/ # 角色 B(普通用户)│ ├─ user/│ │ ├─ profile.json│ │ └─ menu.json│ └─ order/│ └─ list.json├─ common/ # 公共数据(可选)│ └─ city.json└─ README.md # 接口说明(自动生成)
零配置、零依赖、零学习成本——目录即契约,复制即可用!
以我之思,借AI之力!
