花 10 分钟,插入 100 行代码,0 额外依赖,让你项目支持 mock,支持任意 webpack 工程,简单易用前端
目前已将公司传统 PC 项目和 React-Native 项目进行了改造,效果不错,特地分享出来node
写了一个小 demo github.com/imaoda/gene… 供体验webpack
常规 mock 的手段有:ios
这些缘由阻碍了咱们 mock 的脚步:git
本方案的优点:github
本质上仍是请求拦截,并不是新花样,重点在后面的细节的处理技巧,会让这个 mock 方案变得丝滑易用web
常见的请求好比 axios
、fetch
、wx.request
,并非全部请求都有拦截器方法,何况拦截器也不是万能的,一般咱们的项目都会进行 请求再封装,作诸如如下的事情:chrome
好比咱们封装 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/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,方式有不少中,整体来讲,最佳实践就是在 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
// xx.js 文件
export default [
{ url: '/user/id', data: 10086, code: 0 },
{ url: '/user/name', data: 'wyf', code: 0 },
];
复制代码
yarn mock
(具体根据你刚才配置的命令至此,主要内容已经完成,若是还想在项目中继续优化探索,能够继续看下面的部分
ignore 以后,mock 文件夹不会被 git 仓库收录,也就是多人合做开发的时候,你们的 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;
复制代码
本方案是一个很是基础的工程方案,同时因为它没有过多依赖,因此很是灵活,你彻底能够在拦截阶段,对获取的数据进行处理,好比自定义一些模板语法
本方案的优点,在于灵活,不只仅对 XHR 请求进行拦截,任意环境,好比小程序、fetch 等