Skip to content
On this page

Mock Service Worker

官方网站

https://mswjs.io/

安装

bash
yarn add -D msw

browser.js

必须是/src/mocks/browser.js这个路径,且内容不需修改。

js
import { setupWorker } from 'msw';
const reqAll = require.context('./modules', true, /\.js$/);
const handlers = reqAll.keys().reduce((total, key) => {
  return total.concat(reqAll(key).default || reqAll(key));
}, []);
export const worker = setupWorker(...handlers);

Modules

/src/mocks/modules/下创建若干个模块,为方便维护,我建议完全对应/src/api/里的目录结构。

如果需要随机的 Mock Data,可以另安装mockjs模块。如何使用mockjs是另外的知识,可以参考官方手册

/utils/目录的介绍见下文。

js
// 以 /src/mocks/modules/users.js 为例
import createIdSeries from '../utils/createIdSeries';
import createPagedListHandler from '../utils/createPagedListHandler';
var Mock = require('mockjs');
const dataList = createIdSeries(1, 87).map((id) => {
  return {
    id,
    realName: Mock.Random.cname(),
    // 测试时为了命中搜索关键词,特别设了一个字段
    keyword: [1, 2, 3, 4, 5][Math.floor(Math.random() * 5)],
  };
});

export default [createPagedListHandler('/prod-api/users', dataList)];

工具函数

为了便捷开发,可以在/src/mocks/utils/下创建工具函数,比如我的 2 个工具函数:

  1. 生成 id 等差数列
js
// /src/mocks/utils/createIdSeries.js
export default function (from, to) {
  return Array(to - from + 1)
    .fill()
    .map((e, i) => from + i);
}
  1. 生成分页列表处理器,只需传入 2 个参数,第一个是 URL 的路径部分,第二个是数据列表。
js
// /src/mocks/utils/createPagedListHandler.js
import { rest } from 'msw';
export default function (url, dataList) {
  return rest.all(url, (req, res, ctx) => {
    const searchParams = req.url.searchParams;
    switch (req.method) {
      case 'GET':
        // 想要获取报错的响应,就给 URL 末尾加上 fail=1 参数
        if (req.url.searchParams.has('fail')) {
          return res(
            ctx.json({
              code: -1,
              msg: 'Error',
            })
          );
        }

        // GET 分页列表
        if (searchParams.has('pageNum')) {
          const total = dataList.length;
          let pageNum = Number(searchParams.get('pageNum'));
          const pageSize = Number(searchParams.get('pageSize'));
          pageNum =
            pageNum <= Math.ceil(total / pageSize)
              ? pageNum
              : Math.ceil(total / pageSize);
          const start = (pageNum - 1) * pageSize;
          const end = pageNum * pageSize >= total ? total : pageNum * pageSize;
          let list = dataList;
          // 判断是否有搜索词
          for (let pair of searchParams) {
            if (!['pageNum', 'pageSize'].includes(pair[0])) {
              list = list.filter((v) => v[pair[0]] == pair[1]);
            }
          }
          return res(
            ctx.json({
              code: 0,
              msg: '',
              data: {
                pageNum,
                pageSize,
                total,
                list: list.slice(start, end),
              },
            })
          );
        }
        // 根据 id 取单条
        else if (searchParams.has('id')) {
          return res(
            ctx.json({
              code: 0,
              msg: '',
              data: dataList.find((v) => v.id == searchParams.get('id')),
            })
          );
        }
        break;
      // 新增
      case 'POST':
        var payload = req.json();
        const newId = dataList[dataList.length - 1].id + 1;
        dataList.push({
          id: newId,
          realName: payload.realName,
          keyword: payload.keyword,
        });
        return res(
          ctx.json({
            code: 0,
            msg: '',
            data: { id: newId },
          })
        );
      // 修改
      case 'PUT':
        var payload = req.json();
        const row = dataList.find((v) => v.id == payload.id);
        for (let key in payload) {
          row[key] = payload[key];
        }
        return res(
          ctx.json({
            code: 0,
            msg: '',
            data: { id: row.id },
          })
        );
      // 删除
      case 'DELETE':
        const id = Number(searchParams.get('id'));
        const index = dataList.findIndex((v) => v.id === id);
        dataList.splice(index, 1);
        return res(
          ctx.json({
            code: 0,
            msg: '',
            data: { id },
          })
        );
    }
  });
}

/public/目录写入文件

在项目根目录执行下方命令,它会给/public/目录写入一个文件。

bash
npx msw init public/ --save

修改main.js

main.js最顶部写入:

js
if (process.env.NODE_ENV === 'development') {
  const { worker } = require('./mocks/browser');
  worker.start({ onUnhandledRequest: 'bypass' });
}

注意事项

Service Worker 生效前提

  • https 协议下,证书必须合法才能生效。

  • http 协议下,域名必须是localhost才能生效。

杨亮的前端解决方案