我们知道 ant-design-pro 引入了 umi 进行数据模拟, 但是这依然需要后端的支持. 但是在某些应用场景下, 尤其是在我们没有后台服务器的情况下, 想要进行演示的话, 就要考虑进行纯粹的前端模拟, 为此我们引入 mockjs.
本文的所有代码已托管.
GitHub 戳此查看
gitee 用户点击这里
我用 mockjs 是为了进行 gh-pages 的托管, 效果可以点此查看
代理 umi 的 mock 数据
在 umi 里约定 mock 文件夹下的文件或者 page(s) 文件夹下的_mock 文件即 mock 文件, 在此我们希望在非侵入式的情况下优雅的使用 mockjs 代理这些文件. 我们知道计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决, 因此初步的想法是增加一个代理动态的导入 umi 约定的 mock 模块, 然后解析并改装成 mockjs 的语法.
实现 require.context
在前端工程自动化中, 我们经常用到 webpack 的 require.context 进行模块的自动化加载, 这能够满足我们导入 mock 模块的需求, 但是 require.context 是 webpack 的函数, 属于开发依赖包, 运行时不存在此函数, 需要我们自行实现, 代码如下:
- if (typeof require.context === 'undefined') {
- require.context = (base = '.', scanSubDirectories = false, regularExpression = /\.[jt]s$/) => {
- const files = {};
- function readDirectory(directory) {
- fs.readdirSync(directory).forEach(file => {
- const fullPath = resolve(directory, file);
- if (fs.statSync(fullPath).isDirectory()) {
- if (scanSubDirectories) readDirectory(fullPath);
- return;
- }
- if (!regularExpression.test(fullPath)) return;
- files[fullPath] = true;
- });
- }
- readDirectory(resolve(__dirname, base));
- function Module(file) {
- // eslint-disable-next-line global-require,import/no-dynamic-require
- return require(file);
- }
- Module.keys = () => Object.keys(files);
- return Module;
- };
- }
动态加载约定的 mock 模块
这边就是干两件事
加载 mock 文件夹下的模块, 加载 pages 下文件名为_mock 的模块
将加载的 mock 对象合并为一个对象
- let mocks = {};
- const modulesFiles = [
- require.context('./', true),
- require.context('../src/pages/', true, /_mock\.[jt]s$/),
- ];
- const tmp = [];
- modulesFiles.forEach(x => {
- // eslint-disable-next-line array-callback-return
- x.keys().reduce((modules, modulePath) => {
- const m = modulePath.replace(/\.[jt]s/g, '');
- if (!tmp.includes(m)) {
- const value = x(modulePath);
- if (value.default !== undefined) {
- mocks = Object.assign(mocks, value.default);
- }
- tmp.push(m);
- }
- }, {});
- });
转换 mock 为 mockjs
如果 mock 接口返回的是非函数, 则认为是接口返回的数据, 直接交给 mockjs 处理
如果 mock 接口为函数, 则进行函数调用之后交给 mockjs
- function mockXHR() {
- Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send;
- Mock.XHR.prototype.send = () => {
- if (this.custom.xhr) {
- this.custom.xhr.withCredentials = this.withCredentials || false;
- if (this.responseType) {
- this.custom.xhr.responseType = this.responseType;
- }
- }
- // eslint-disable-next-line prefer-REST-params
- this.proxy_send(...arguments);
- };
- function XHR2ExpressReqWrap(respond) {
- return options => {
- let result = null;
- if (respond instanceof Function) {
- const { body = '{}', method, url } = options;
- // https://expressjs.com/en/4x/api.html#req
- result = respond(
- {
- url: options.url,
- method,
- body: JSON.parse(body),
- query: param2Obj(url),
- },
- undefined,
- options.url,
- {
- body: JSON.parse(body),
- },
- );
- } else {
- result = respond;
- }
- return Mock.mock(result);
- };
- }
- Object.keys(mocks).forEach(i => {
- let url = i;
- let method = 'GET';
- const res = /^(GET|POST|DELETE|PUT) /.exec(i.toUpperCase());
- if (res && res.length === 2) {
- // eslint-disable-next-line prefer-destructuring
- method = res[1];
- url = url.substring(method.length + 1);
- }
- Mock.mock(new RegExp(url), method, XHR2ExpressReqWrap(mocks[i]));
- });
- }
- // 调用转换器
- mockXHR();
增加 mock fetch 的支持
运行之后我们发现实际上浏览器还是向后端发出了请求, 并没有被拦截. 发现原来 mockjs 仅仅支持 Ajax 的请求拦截, 而 umi 使用的是 fetch, 并不被 mockjs 所支持, 在此我们需求增加另外一层代理, 使得 mockjs 支持 fetch. 为此, 在 GitHub 上找到了一个叫做 mockjs-fetch 的库, 下载下来稍加改造即可.
结语
至此我们可以在不改变 ant-design-pro 的框架下, 使用 mockjs 进行请求拦截, 并且随时可拔插.
来源: http://www.jianshu.com/p/fb0de8a9f115