0依赖 100行代码,为项目添加 mock

写在前面的话

花 10 分钟,插入 100 行代码,0 额外依赖,让你项目支持 mock,支持任意 webpack 工程,简单易用前端

目前已将公司传统 PC 项目和 React-Native 项目进行了改造,效果不错,特地分享出来node

写了一个小 demo github.com/imaoda/gene… 供体验webpack

本方案的优点

常规 mock 的手段有:ios

  • 利用 webpack 的 devSever
  • 启动 node 层的服务
  • 利用 charles 代理
  • 利用 chrome 插件去代理到 mock 服务
  • 拦截请求 (本方案)

这些缘由阻碍了咱们 mock 的脚步:git

  • 须要各类配置,用起来太麻烦
  • 学习成本高,配置不对不生效
  • React-Native小程序 等环境用不了
  • 移动端真机调试 mock 不了
  • 对项目新人有学习和配置成本

本方案的优点:github

  • 无复杂配置
  • 0 依赖
  • 傻瓜式的使用体验
  • 基于老项目改造很容易 (我用了不到10分钟)
  • mock 数据在项目集中管理维护
  • 即配置即生效
  • 支持全部类型的请求 (xhr fetch wx.request ...)
  • 不影响打包体积
  • 生产环境不受影响

方案原理

本质上仍是请求拦截,并不是新花样,重点在后面的细节的处理技巧,会让这个 mock 方案变得丝滑易用web

常见的请求好比 axiosfetchwx.request,并非全部请求都有拦截器方法,何况拦截器也不是万能的,一般咱们的项目都会进行 请求再封装,作诸如如下的事情:chrome

  • 根据环境填充 baseURL
  • cookie / storage 搬运
  • 异常状况弹窗
  • 移动端维护请求队列显示 loading 菊花
  • fetch 提供 timeout
  • 今天的主角 mock

好比咱们封装 fetch,让请求支持 mockdocker

import mockList from '../../mock'; // 引入写好的 mock 数据

// 封装 fetch,若是 url 命中 url 则直接返回 mock 数据,不然走正常请求
export default async function request(url) {
  const resArr = mockList.filter(item => item.url === url); 
  return resArr[0] || (await fetch(url).then(i => i.json()));
}
复制代码

处理技巧

如何批量引入 mock 数据?

咱们会将整个 mock 文件夹下文件引入,做为 mock 数据集,好比,咱们在 mock/index.js 下引入全部其余文件,并合并导出,以下图:json

麻烦的事来了,一旦目录结构发生变化,好比新增,删除,批量调整,嵌套文件夹等,咱们都须要频繁的修改 mock/index.js 文件,引入这些数据,并合并,再导出

那么怎么才能方便的全量引入呢?

这时,有同窗可能会说:用 fs.readdirSync 读取整个 mock 文件夹呀!

思路是对的,可是没法成行;由于毕竟这是前端工程,而非 node 工程,fs.readdirSync 是运行时处理的函数,而前端工程的运行时已经在浏览器端了,浏览器端何来的 fs?

咱们能够利用 require.context 来解决,该 API 既非 commonjs 语法,也不是 ES module 语法,而是由 webpack 提供的

webpack 在编译时,词法解析到该 api,会将这段代码放入 node 运行时去 执行,并将结果拼接到打包好的 module

使用 require.context 进行批量引入

前面啰嗦了半天理论,接下来使用 require.context 来引入。该套路比较固定,所以,不用刻意去理解

let routes = [] // 收集全部 mock 数据

// 遍历目录下 mock,开启递归遍历,匹配 (js|ts|jsx|tsx) 结尾的文件
const ctx = require.context('../../mock', true, /(js|ts|jsx|tsx)$/);

// 对命中的文件进行引入,用 cjs 语法引入 esm 导出的,需加 .default
ctx.keys().forEach((file) => {
    const content = ctx(file).default;
    if (content instanceof Array) {
        routes = [...routes, ...content];
    } else console.warn(`mock 文件${file}格式不是数组`);
});

// 导出全部
export default routes;
复制代码

开启 mock 的技巧

如何开启、关闭 mock,方式有不少中,整体来讲,最佳实践就是在 package.json 的 script 里新增一个命令,好比

"script": {
  "build": "...",
  "dev": "...",
  "mock": "xxx dev --mock"
}
复制代码

经过 webpack 的 definePlugin 为代码注入变量,告知是否走的 yarn mock 命令启动的调试:

// webpack 配置中,plugins 里增长:
new webpack.DefinePlugin({ __IS_MOCK__, process.argv.includes('--mock') }) // 简单起见就不用 minimist 解析参数了
复制代码

此时,业务代码里已经注入了常量 __IS_MOCK__

最后,咱们再小改动一下以前封装的 request

export default async function request(url) {
  // 若是未开启 mock 直接返回
  if(!__IS_MOCK__) return await fetch(url).then(i => i.json())
  
  const resArr = mockList.filter(item => item.url === url); 
  return resArr[0] || (await fetch(url).then(i => i.json()));
}
复制代码

配置好了,开始使用!

提交到 git 仓库上,任意组员拉下来,只需 2 步,开启 mock

  1. 在 mock 文件夹下建立文件,内容如
// xx.js 文件
export default [
  { url: '/user/id', data: 10086, code: 0 },
  { url: '/user/name', data: 'wyf', code: 0 },
];
复制代码
  1. yarn mock (具体根据你刚才配置的命令

其余深刻的探讨点

至此,主要内容已经完成,若是还想在项目中继续优化探索,能够继续看下面的部分

是否 gitignore mock 文件夹

ignore 以后,mock 文件夹不会被 git 仓库收录,也就是多人合做开发的时候,你们的 mock 文件互不干扰。

我我的理解:

  • 适合 git 收录的场景:你的项目过于依赖 mock,好比后端环境常常挂,或者在浏览器环境没有宿主提供的鉴权信息(如 app 会给 webview 带一些登录 token 等),致使没法获取数据
  • 不适合 git 收录的场景:好比你的项目只在新需求新增几个接口,供后端没有 ready 时 mock,其余接口正常走请求

值得注意的是,若是 ignore 掉了 mock 文件夹,require.context 会报错,此时加上 try catch,webpack 就会在找不到 mock 文件夹时跳过不处理,如:

let routes = [] // 收集全部 mock 数据

try {
  const ctx = require.context('../../mock', true, /(js|ts|jsx|tsx)$/);
  /** 略 **/
} catch (e) {}

// 导出全部
export default routes;
复制代码

是否影响打包的包大小

若是工程 gitignore 了 mock 文件夹,上传的到 docker 构建的时候,是没有该文件夹的,所以不会影响到打包的体积

若是没有 ignore,且不作任何处理,很不幸的是,你的最终 bundle 会带上这些 mock 数据。解决方案就是在执行 require.context 前加入 if 条件,仅在 mock 打开的时候打包,如:

let routes = [] // 收集全部 mock 数据

try {
  if(__IS_MOCK__) {
     const ctx = require.context('../../mock', true, /(js|ts|jsx|tsx)$/);
     /** 略 **/
  }
} catch (e) {}

// 导出全部
export default routes;
复制代码

是否支持随机生成 mock 数据等进阶需求

本方案是一个很是基础的工程方案,同时因为它没有过多依赖,因此很是灵活,你彻底能够在拦截阶段,对获取的数据进行处理,好比自定义一些模板语法

本方案的优点,在于灵活,不只仅对 XHR 请求进行拦截,任意环境,好比小程序、fetch 等

github推文地址-js实践

相关文章
相关标签/搜索