背景

前端开发在调试 api 阶段,或者在 fix bug 时, 经常会遇到需要不断切换代理环境的问题, 很让人头大。所以一个灵活的代理逻辑能让你省去好几支烟的功夫。

下面记录一次项目中代理的进化方案。

初始代理方案

通过环境变量来确定代理方向环境,

const getEndpoint = () => {
  switch (process.env.EP) {
    case 'qa':
      return 'https://qa.xxx.cn';

    case 'online':
      return 'https://xxx.cn';

    case 'dev':
      return 'https://test.xxx.cn';

    default:
      return 'http://dev2.xxx.cn';
  }
};

module.exports = {
  '/api': {
    target: endpoint,
    changeOrigin: true,
    headers: {
      Host: new URL(endpoint).hostname
    }
  }
};

react-scripts 默认生效的代理文件 /src/setupProxy.js:

module.exports = function(app) {
  Object.entries(apiProxyConfig).forEach(([key, value]) => {
    app.use(key, proxy(value));
  });
};

这种方式,在开发环境启动的过程中就确定了代理环境,且启动后无法改变代理环境, 除非修改环境变量,重启服务。当业务模块不断增加,开发环境启动的时间也越来越长,每次需要频繁切换环境验证 bug 时, 就会很耗时间。

所以需要进化一下,帮我们省一支烟的时间。

进化后代理方案

介绍 modheader

Chrome 浏览器扩展,主要用来 添加/修改/删除请求和响应头根据URL Pattern来只对特定网站生效,等等用法。如下图,详见modheader

image-20200811131639316

原理

通过浏览器对请求信息的劫持再编辑,通过读取请求头自定义添加的值来确定代理的环境, 这样就把代理环境的做成动态的,可通过浏览器插件自定义切换代理环境了。

module.exports = function (app) {
  const map = new Map();

  Object.entries(apiProxyConfig).forEach(([key, value]) => {
    app.use(key, (req, res, next) => {
      const endpoint = req.headers['x-api-endpoint'];

      if (endpoint && /^https?:\/\//.test(endpoint)) {
        let handler = null;

        if (map.has(endpoint)) {
          handler = map.get(endpoint);
        } else {
          handler = createProxyMiddleware({
            target: endpoint,
            changeOrigin: true,
            headers: {
              Host: new URL(endpoint).hostname
            },
            pathRewrite: '^/api'
          });
          map.set(endpoint, handler);
        }

        const path = req.url;
        const prefix = '/api';
        // eslint-disable-next-line no-console
        console.log(`[Proxy] /api${path} => ${endpoint}${prefix}${path}`);

        return handler(req, res, next);
      }

      return next();
    });
  });
};

多人协作

为避免不同开发者重复配置 modheader ,需要将 modheader 配置推广之,恰好modheader支持导出和导入,如图:

image-20200811132951612

所以只需一人配置,分享链接给其他人导入即可使用。

本地 mock 数据

本地 mock 和代理环境并存。

module.exports = function (app) {
  const mockFile = resolve(__dirname, '../mock/mock.js');

  if (existsSync(mockFile)) {
    apiMocker(app, mockFile);
  }

  const map = new Map();

  Object.entries(apiProxyConfig).forEach(([key, value]) => {
    // ...
  })
}

后记

以上方案目前完全能满足开发需求,且方便易用。

欢迎大家评论你们业务中的前端环境代理。


更复杂的代理,推荐文章如下:

一文搞定前端代理骚操作!再也不怕线上bug啦!


ling
1.1k 声望314 粉丝