开发过程中如何在多个环境中优雅的切换

04/13/2024

背景

最近我又接了好多需求,每个需求有对应着不同的后端,也对应着不同的开发/测试/预发环境。

同时吧,我还有几个线上bug需要修复,有个是需要连接我们saas,其他的还需要去连接客户的环境。

每次切换环境都需要修改一下webpack配置里的proxy,然后重新启动,好麻烦呀。

怎么简单又优雅的去切换环境呢?

webpack 的 proxy

首先,我们需要了解 webpack 的proxy 是如何工作的。

webpack的proxy,本质上是一个webpack的plugin,在配置中有devServer.proxy时,就会加载HttpUriPlugin。
有请求接入就会经过HttpUriPlugin,进行代理转发,或是直接请求。

所以,假使

我们有这样一个plugin

这个plugin在接收到一个特定请求时,比如 http://localhost:8000/proxyUpdate?target=google.com
他就会将proxytarget重新设定为google.com

这个plugin在接收到一系列带有特殊开头的请求时,比如 http://localhost:8000/api/where-are-you-from时,
他就会将这一系列请求转发给之前设定的那个host

对于其他的请求,比如 http://localhost:8000/a74bvf34.js,则会直接请求不进行转发。

看上去,除了重新设定target,其他的都与proxy完全一致呀。

实现

对于webpack的话,有两种实现方法,写一个plugin,或是写一个middleware。

察看二者API,会发现,middleware不就是跟express的middleware完全一样的吗~

here I choose middleware way.

首先,先翻看一下webpack关于middleware的API,具体代码如下

module.exports = {
  // ...
  devServer: {
    setupMiddlewares: (middlewares, devServer) => {
      if (!devServer) {
        throw new Error('webpack-dev-server is not defined');
      }
 
      devServer.app.get('/setup-middleware/some/path', (_, response) => {
        response.send('setup-middlewares option GET');
      });
 
      // Use the `unshift` method if you want to run a middleware before all other middlewares
      // or when you are migrating from the `onBeforeSetupMiddleware` option
      middlewares.unshift({
        name: 'first-in-array',
        // `path` is optional
        path: '/foo/path',
        middleware: (req, res) => {
          res.send('Foo!');
        },
      });
 
      // Use the `push` method if you want to run a middleware after all other middlewares
      // or when you are migrating from the `onAfterSetupMiddleware` option
      middlewares.push({
        name: 'hello-world-test-one',
        // `path` is optional
        path: '/foo/bar',
        middleware: (req, res) => {
          res.send('Foo Bar!');
        },
      });
 
      middlewares.push((req, res) => {
        res.send('Hello World!');
      });
 
      return middlewares;
    },
  },
};

根据官方API,可以轻松写出来关于切换target(实际上是修改一个变量)的middleware

middlewares.unshift({
  name: 'proxy-update',
  path: '/proxyUpdate',
  middleware: (req, res) => {
     process.env.PROXY_UPDATE_URL = req.query.target;
  },
});

那,proxy的middleware该怎么写呢?
可以自己完全重写,类似于上面,不过要考虑很多方面,比如说changeOrigin,useWs等等。
这边给大家推荐一个npm库http-proxy-middleware,里面的createProxyMiddleware可以很轻松地帮我们解决这些小问题啦。

代码可以参考如下

const proxyMiddleware: any = createProxyMiddleware('/api', {
  target: '/',
  changeOrigin: true,
  ws: true,
  router: () => {
    return process.env.PROXY_UPDATE_URL!;
  },
});
 
middlewares.push(proxyMiddleware);

如此一来,只需要访问 http://localhost:8000/proxyUpdate?target=google.com 就可以切换代理host啦~~~

更多

在实践中,我们新建了一个chrome extension 插件。
在这个插件里,我们可以随时和平台方同步各个环境的数据(host, username, password, token),以及自己可以去修改、添加一些其他环境、账号信息,并且同步给平台。

我们的登录信息是保存在本地localstorage的,在插件中具体流程是

如果当前页面是本地开发环境的话

  • 请求登陆接口获取token
  • 写入localstorage
  • 切换proxy target
  • 刷新页面(当前为登陆页面则重定向到首页)

如果非本地开发环境

  • 请求登陆接口获取token
  • 打开新页面,url为该环境的首页url,并且带有登陆参数