为什么需要Mock数据
前端开发依赖后端接口时的阻塞问题
独立开发和测试的需求
快速迭代和原型验证的重要性
当前版本及框架
React18
Umi 4.0
Ant Design + Ant Design Pro
其实这些都不重要,主要是有Umijs,因为Umijs具有开箱即用Mock功能的能力,可参考官网文档:UmiJS-Mock
实践
前提
如果你想使用Umi里面的mock,那么必须安装Umi框架,用npm或者pnpm就正常安装,此步骤不在赘述,以下直接实践
第一步 创建一个mock文件

不用纠结这个 文件是放在src下面,还是谁的下面,直接就这个项目里面,与src平级关系
第二步 在mock创建对应的ts文件,放置模拟数据和接口
mock/ip.ts
typescript
const MOCK_DATA = [
{
id: '1',
used: 10,
total: 14,
region: '洛杉矶',
coreRoute: 'US',
exitNode: 'Uplink',
remark: 'TikTok直播专用',
},
{
id: '2',
used: 13,
total: 14,
region: '洛杉矶',
coreRoute: 'US',
exitNode: 'Uplink',
remark: 'TikTok直播专用',
},
{
id: '3',
used: 0,
total: 14,
region: '洛杉矶',
coreRoute: 'US',
exitNode: 'Uplink',
remark: 'Amazon电商专用',
}
];
// 抽屉列表的专用数据源(按段 id 区分)
const IP_PERMISSION_DATA: Record<string, any[]> = {
......// 数组包裹数组
};
export default {
// 列表(保留)
'POST /api/ip/list': (req: any, res: any) => {
setTimeout(() => {
res.send({ success: true, data: MOCK_DATA, total: MOCK_DATA.length });
}, 120);
},
// 获取单条详情:根据 body.id 返回对应 record
'POST /api/ip/detail': (req: any, res: any) => {
const { id } = req.body || {};
const item = MOCK_DATA.find((it) => String(it.id) === String(id));
setTimeout(() => {
res.send({ success: true, data: item || null });
}, 80);
},
// 更新(简单模拟,body 包含 id 与其他字段)
'POST /api/ip/update': (req: any, res: any) => {
const payload = req.body || {};
const idx = MOCK_DATA.findIndex((it) => String(it.id) === String(payload.id));
if (idx >= 0) {
MOCK_DATA[idx] = { ...MOCK_DATA[idx], ...payload };
}
setTimeout(() => {
res.send({ success: true, message: '更新成功(mock)', data: MOCK_DATA[idx] || null });
}, 120);
},
// 权限管理抽屉列表
'POST /api/ip/children': (req: any, res: any) => {
const { id, cidr } = req.body || {};
let key = id ? String(id) : undefined;
if (!key && cidr) {
const found = MOCK_DATA.find((it) => String(it.cidr) === String(cidr));
key = found?.id;
}
const data = (key && IP_PERMISSION_DATA[key]) || [];
setTimeout(() => res.send({ success: true, data, total: data.length }), 100);
},
};
第三步 调用mock数据和接口
放在某个调用接口的.tsx文件里面
typescript
// 列表数据(来自 /mock/ip.ts)
const [ipList, setIpList] = useState<any[]>([]);
// 加载列表(调用 mock 的 GET /api/ip/list,后端到位时直接替换)
const loadIpList = useCallback(
async (opts?: { page?: number; pageSize?: number }) => {
const page = opts?.page || currentPage;
const size = opts?.pageSize || pageSize;
const payload = {
page,
pageSize: size,
keyword: keyWord || '',
source: filterSource || '',
coreRoute: filterCoreRoute || '',
};
try {
const res = await fetch('/api/ip/list', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
// 期望后端/Mock 返回 JSON 格式 { success: true, data: [...], total: n }
const ct = (res.headers.get('content-type') || '').toLowerCase();
if (!ct.includes('application/json')) {
console.warn('loadIpList non-json response');
setIpList([]);
setTotal(0);
return;
}
const json = await res.json();
if (json && json.success) {
setIpList(json.data || []);
setTotal(json.total || 0);
} else if (json && Array.isArray(json.data)) {
// 兜底兼容
setIpList(json.data || []);
setTotal(json.total || json.data.length || 0);
} else {
setIpList([]);
setTotal(0);
}
} catch (err) {
console.error('loadIpList error', err);
setIpList([]);
setTotal(0);
}
},
[currentPage, pageSize, keyWord, filterSource, filterCoreRoute],
);
// 获取单条详情(调用 mock 的 POST /api/ip/detail)
const getIpDetail = useCallback(async (id: string | number) => {
try {
const res = await fetch('/api/ip/detail', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id }),
});
const json = await res.json();
if (json && json.success) return json.data;
return null;
} catch (err) {
console.error('getIpDetail error', err);
return null;
}
}, []);
// 更新 IP 段(调用 mock 的 POST /api/ip/update),成功后刷新列表
const updateIp = useCallback(
async (payload: any) => {
try {
const res = await fetch('/api/ip/update', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const json = await res.json();
if (json && json.success) {
// 刷新当前页
loadIpList({ page: currentPage, pageSize });
return { ok: true, data: json.data };
}
return { ok: false, error: json };
} catch (err) {
console.error('updateIp error', err);
return { ok: false, error: err };
}
},
[loadIpList, currentPage, pageSize],
);
typescript
// 比如这个列表接口调用
useEffect(() => {
loadIpList();
}, [currentPage, pageSize, filterSource, filterRegion, keyWord]);

第四步 注意是否开启mock
以我此刻的项目为例,
npm run start