提高前端开发效率:实现代理服务器配置的热更新

157 阅读2分钟

在前端开发过程中,我们常常需要为开发服务器设置本地代理。然而,对于目标代理服务器频繁变动的环境(如开发流程不规范或需要频繁排查线上故障),这可能导致一个痛点:每次修改代理配置后都需要重启开发服务器(如 Vite 或 Webpack), 而这个重启过程通常比较耗时,这会显著影响开发效率。本文将探讨如何实现热更新代理服务器配置,以解决这个问题。

思路

建立一个代理服务器作为开发服务器的上游地址,代理上游服务,上游服务地址存放于一个简单的配置文件mock/server.conf

proxy ^/api/(.*?)$ http://(A|B).user.domain/$1

修改mock/server.conf的内容,新来的接口请求就自动被导向新的上游地址,开发服务器的上游地址其实指向的是这个代理服务器,也就不需要重启了。

graph LR
    开发服务器 --> 代理服务器
    代理服务器 --> 上游服务A
    代理服务器 --> 上游服务B

实现过程

第一步

建立代理服务器

const express = require('express');
app.use(proxyParserMiddleware())
app.listen(9090, function () {
    console.log(' Listening on http://127.0.0.1:%d', port)
  })

第二步

实现上述proxy配置的dsl解析

function Parser(file) {
  var rules = [];

  function Ruler(type, reg, to) {
    return {
      type: type,
      reg: reg,
      to: to
    };
  }

  if (!Array.isArray(file)) {
    file = [file];
  }

  file.forEach(function(file) {
    if (!fs.existsSync(file)) {
      return null;
    }

    var content = fs.readFileSync(file, 'utf-8');
    var lines = content.split(/\r\n|\n/);
    var rrule = /^rewrite\s+([^\s]+)\s+([^\s]+)$/i;

    lines.forEach(function(line) {
      var m = rrule.exec(line);

      if (!m) {
        return;
      }

      rules.push(new Ruler(m[1].toLowerCase(), new RegExp(m[2], 'i'), m[3]));
    });
  });

  return {
    match: function(url) {
      var found;

      var arr = [url.path, url.pathname];

      rules.every(function(ruler) {
        arr.every(function(url) {
          var m = url.match(ruler.reg);

          if (m) {
            found = ruler;
            found.match = m;
            return false;
          }

          return !found;
        });

        return !found;
      });

      return found;
    }
  };
}

第三步

热重启中间件实现

const { createProxyMiddleware } = require('http-proxy-middleware');
var parseUrl = require('url').parse;

function proxyParserMiddleware(req,res,next){
    // **重点**,每次接口请求都会重新解析配置,所以修改server.conf文件的内容实时生效的
    const parser = Parser('mock/server.conf');
    const rule = parser.match(parseUrl(req.url));

    if(rule) {
        var to = ruler.to.replace(/\$(\d+)/g, function(all, index) {
            return ruler.match[index] || '';
        });

        var target = parseUrl(to);
          req.originalUrl = req.originalUrl || req.url;
          req.url =
            target.path +
            (target.search
              ? url.query
                ? '&' + url.query
                : ''
              : url.search || '');
          createProxyMiddleware({
            target: target.protocol + '//' + target.host,
            changeOrigin: true
            // ws: true,
          })(req, res, next);
    }else {
        next()
    }
}

使用方式

增加vite开发服务器配置

export default {
    server:{
        proxy: {
            "/api": `http://127.0.0.1:9090`,
        }
}

针对复杂的情形,比如有多个后台服务,我们可以修改一下mock/server.conf

proxy ^/api/user(.*?)$ http://user.domain/$1
proxy ^/api/order(.*?)$ http://order.domain/$1

通过正则表达式的匹配可以支持非常灵活强大的配置,最重要的是修改后立即生效,前端不用担心后端服务地址的变化,不需要重启

后话

目前我们只实现了一个简单的proxy配置,而在实际开发中我们还面临除后端地址热更新之外的其它需求,比如设计一个更高效的mock服务、资源和接口缓存、资源内容劫持和篡改、接口数据上报等等其他功能。基于这个代理服务器,我们可以开发出很多强大的功能,助力工程效率。
这个代理服务器除了支持热更新以外,它的使用对业务模块没有侵入,作为bff层能提供非常丰富的功能。
基于这个想法,作者开发了@yakies/cli这个包,提供了更多的丰富功能,后续也会写一些文章来说明。