为你的cra项目接入mock代理

232 阅读3分钟

等春风得意,等时间嘉许

我们通常通过create-react-app 脚手架创建出来的项目是没有mock数据的。但通过mock数据可以帮助我们快速测试,独立于后端开发,降低前后端之间的耦合度。下面带大家实现一个简单的mock数据,也可以在此基础上根据自己的业务进一步扩展

WebpackDevServer

我们先通过cra 脚手架创建一个React项目,并暴露出原本的webpack配置文件

npx create-react-app cra_mock

npm run eject

npm install mockjs

当我们执行npm start 时,它会执行scripts/start.js 下面的脚本。它会读取webpackDevServer.config.js下面的文件,从而创建一个WebpackDevServer的实例

const WebpackDevServer = require('webpack-dev-server');
const createDevServerConfig = require('../config/webpackDevServer.config');

const serverConfig = {
  ...createDevServerConfig(proxyConfig, urls.lanUrlForConfig),
  host: HOST,
  port,
};

const compiler = createCompiler({
  appName,
  config,
  urls,
  useYarn,
  useTypeScript,
  webpack,
});

const devServer = new WebpackDevServer(serverConfig, compiler);

webpack-dev-server底层是express + webpack-dev-middleware

express是基础,webpack-dev-middleware是中间件,以监听模式启动 webpack,将编译后的文件输出到内存(使用fs-memory),通过webpack的HRM(热替换 Hot Module Replacement)API便于本地开发的实时更新。

const express = require('express');
const webpackDevMiddleware= require('webpack-dev-middleware');
const app = express();
const devServer = app.use( webpackDevMiddleware(config) )

看到这里,我们是不是就能想到,既然express可以使用webpack-dev-middleware的中间件,那我们是不是也可以自定义一个中间件,来扩展WebpackDevServer的能力呢?

当然,我们可以自定义一个中间件,来进行本地代理mock数据

自定义中间件

webpack.docschina.org/configurati…

我们修改一下webpackDevServer.config.js配置文件,通过devServer.setupMiddlewares这个接口,可以自定义我们的中间件。

 setupMiddlewares(middlewares, { app }) {
  app.use(mockServiceMiddleware());
  return middlewares;
}

mockServiceMiddleware实现

我们要实现的中间件功能如下:

第一步:我们要判断请求的url中是否包含mock,如果包含mock,我们才走本地代理

const mockServiceMiddleware = () => {
    return (request,response,next) => {
        const parsedUrl = url.parse(request.url, true);
        const pathname = parsedUrl.pathname;
        if (/^/mock/.test(pathname)) {
            return proxyToLocal(request, response);
        }
        next();
    }
}

第二步:格式化request数据,获取post请求中body的信息,放入request.body中

const proxyToLocal = (request, response) => {
    const parsedUrl = url.parse(request.url, true);
    request.query = parsedUrl.query;
    const data = [];
    request.on('data', trunk => {
        data.push(trunk && trunk.toString());
    });
    request.on('end', trunk => {
        if (trunk) {
            data.push(trunk.toString());
        }
        request.body = data.join('');
        mockLocalData(request, response);
    });
}

第三步:通过url获取本地mock数据文件

// mock数据存放的目录
const responsePath = path.resolve(process.cwd(), 'mock/response');

const findMockFilePath = (pathname) => {
    if (pathname.indexOf('/mock') === 0) {
        pathname = pathname.slice(5);
    }
    const paths = pathname.split('/');
    const resDataPath = responsePath + '/' + paths[1] + '.js';
    return resDataPath;
}

第四步:读取mock数据

function getMockData(tplPath, options) {
    const tpl = require(tplPath);
    if (typeof tpl === 'function') {
        return Mock.mock(tpl(options));
    }
    else if (typeof tpl === 'object') {
        return Mock.mock(tpl);
    }
    return {
        code: 1,
        message: 'mock文件需要是object或者function'
    };
}

第五步:通过response.json将mock数据返回

const mockLocalData = (request, response) => {
    const parsedUrl = url.parse(request.url, true);
    const pathname = parsedUrl.pathname;
    const body = request.body;
    const query = parsedUrl.query;
    const options = {
        pathname, body, query
    };
    const mockfile = findMockFilePath(pathname);
    const data = getMockData(mockfile, options);
    response.json(data);
}

Demo

我们来写两个请求来验证一下,分别是get请求和post请求

function App() {
  const getRequest = () => {
    fetch('/mock/list').then((data) => {
      console.log(data);
    })
  }

  const postRequest = () => {
    const data = {
      'name': 'cwl',
    }
    fetch('/mock/table',{
      method:'POST',
      body: JSON.stringify(data),
    })  
  }

  return (
    <div className="App">
      <header className="App-header">
        <button onClick={getRequest}>
          get请求
        </button>

        <br />

        <button onClick={postRequest}>
          post请求
        </button>
      </header>
    </div>
  );
}

export default App;

我们的mock数据统一存放在mock/response这个文件夹下面,新建list.jspost.js两个文件

// list.js
module.exports = {
    "errmsg": "",
    "code": 0,
    "data": {
       "name": "cwl",
       "age": 21,
    }
}
// post.js
module.exports = (options) => {
    const {body} = options;
    const data = JSON.parse(body);
    const {name} = data;
    // 根据post中body请求信息,来进行不同的mock数据
    if(name === 'cwl') {
        return {
            "code": 0,
            "msg": "success",
            "data": {
                "review": "handsom"
            }
        }
    }else {
        return {
            "code": 0,
            "msg": "success",
            "data": {
                "review": "ugly"
            }
        }
    }
}

源代码如下:

github.com/chenwll/cra…

ToDo

  1. 错误及边界情况处理,如文件查询失败等
  2. 模块化,如何根据业务划分mock模块化,划分出不同的mock文件
  3. 能否利用缓存,将读取后的文件放入缓存中,提升性能