在前端开发过程中,我们常常需要为开发服务器设置本地代理。然而,对于目标代理服务器频繁变动的环境(如开发流程不规范或需要频繁排查线上故障),这可能导致一个痛点:每次修改代理配置后都需要重启开发服务器(如 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这个包,提供了更多的丰富功能,后续也会写一些文章来说明。