数据模拟 (Mock) 是前端开发过程中必不可少的一环,是分离前后端开发的关键链路。通过预先跟服务器端约定好的接口,模拟请求数据甚至逻辑,能够让前端开发独立自主,不会被服务端的开发所阻塞。
模拟数据文件存放在工程根目录下的 mocks 文件夹内,文件名后缀支持 .ts
、.js
、.json
。文件匹配的模式是根据 请求路径
自右向左 依次匹配,找到文件即停止。
例如: 当请求 /api/user
时,将依次匹配 user.ts
、api.ts
,如果 user.ts
存在则不再寻找 api.ts
。所以文件的命名应该是 请求路径
中所包含的字符。
通过实现 serve-mock 的 IMock
接口,以获得更多的类型提示:
import { IMock } from 'serve-mock';
const mock: IMock = {
'GET /api/users': [
{ id: 1, name: '曲丽丽' },
{ id: 2, name: '付小小' },
],
'POST /api/users': '新建成功',
'PUT /api/users': '更新成功',
'DELETE /api/users': (res, req) => {
res.statusCode = 204;
res.end();
},
};
export default mock;
注意: 因为 webpack-dev-server 依赖于 Express,所以,当值是函数时将接收由 Express 优化过后的 res 和 req 对象,但是不建议使用它,这是因为 Express 是对 http 的二次封装,其次,服务端渲染时 server.tsx
是通过 http 创建的服务,所以,使用优化后的对象属性及方法是不安全的。
需要对 CURD 操作进行数据模拟时,你可能会这样去写:
import { IMock } from 'serve-mock';
import { parse } from 'url';
let data: any[] = [
{ id: 1, name: 'zhangsan' },
{ id: 2, name: 'lisi' },
];
const mock: IMock = {
// 获取所有用户 -> index
'GET /api/users': (req, res) => {
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.write(JSON.stringify(data));
res.end();
},
// 获取给定用户信息 -> show
'GET /api/users/:id': (req, res) => {
const { query } = parse(req.url, true);
res.setHeader('Content-Type', 'application/json;charset=utf-8');
res.write(JSON.stringify(data.find(u => u.id === query.id)));
res.end();
},
// 创建一个用户 -> create
'POST /api/users': (req, res) => {
res.statusCode = 201;
res.end();
},
// 更新给定用户的信息 -> update
'PUT /api/users/:id': (req, res) => {
res.statusCode = 201;
res.end();
},
// 删除给定用户 -> delete
'DELETE /api/users/:id': (req, res) => {
res.statusCode = 204;
res.end();
},
};
export default mock;
现在,请改用 resource 工具函数吧:
import { resource } from 'serve-mock/utils';
export default resource('/api/users');
更多用法,请参考 serve-mock
通过 res.statusCode 设置错误码即可模拟 http 请求出错
import { IMock } from 'serve-mock';
const mock: IMock = {
'PUT /api/users': (req, res) => {
res.statusCode = 204;
res.end();
},
};
export default mock;
为了更加真实地模拟网络数据请求,往往需要模拟网络延迟时间。通过使用 serve-mock 工具函数,可以实现单个或所有响应的延迟。
import { IMock } from 'serve-mock';
import { delay } from 'serve-mock/utils';
const mock: IMock = {
'PUT /api/users': delay('操作成功', 1000),
};
export default mock;
import { IMock } from 'serve-mock';
import { delay, delays } from 'serve-mock/utils';
const mock: IMock = {
'GET /api/user': {
id: 1,
name: '曲丽丽',
},
'PUT /api/users': '操作成功',
};
export default delays(mock, 1000);
把延迟时间固定在一个值,特别是在所有请求都需要延迟的时候,这样的固定等待与真实网络环境仍有较大的差异。幸运的是 serve-mock 已经考虑到了这一点,通过传递 max
参数并与 min
参数搭配使用,即可实现一个随机范围时间延迟:
delay(value, 10, 1000); // 将延迟时间随机为 10 - 1000 毫秒
delays(mock, 100, 2000); // 将延迟时间随机为 100 - 2000 毫秒
Mock.js 是常用的辅助生成模拟数据的第三方库,借助它可以提升数据模拟的能力。
import { IMock } from 'serve-mock';
import mockjs from 'mockjs';
const mock: IMock = {
'GET /api/tags': mockjs.mock({
'list|100': [{ name: '@city', 'value|1-100': 50, 'type|0-2': 1 }],
}),
};
export default mock;
当本地开发完毕之后,如果服务器的接口已满足约定,那么,只需要关闭数据模拟或者代理到服务端的真实接口地址即可。
通过使用 without-mock
选项来关闭数据模拟:
yarn run dev --env without-mock
当拥有单独的 API 后端开发服务器并且希望在同一域上发送 API 请求时,代理某些 URL 可能会很有用。webpack 开发服务器使用功能强大的 http-proxy-middleware 软件包,在脚手架内只需要将 .proxy.example
文件复制一份并命名成 .proxy.ts
,即可完成代理配置
import type { Configuration } from 'webpack-dev-server';
const proxy: Configuration['proxy'] = {
'/api': 'http://example.com',
};
export default proxy;