搞前端这十年,最烦的事情之一就是折腾本地开发环境。
特别是最近几年,业务越来越重,安全策略也越来越严。比如调个浏览器 Web Crypto API、调摄像头权限、或者处理微前端跨子域的 SameSite Cookie,浏览器直接强制要求 HTTPS 环境。
以往我们怎么做?要么在 Vite / Webpack 里配 https: true(然后每次换项目都要忍受浏览器的大红警告拦截),要么在本地挂一个重量级的 Whistle 或 Charles,再不然就是写一堆复杂的 Node.js 脚本做代理和 Mock。
但这套方案有几个明显的蛋疼点:
- 内存占用大:随便跑个 Node 中间件加上前端 dev server,内存几个 G 就没了。
- Mock 切换繁琐:联调时后端接口挂了,想切本地静态 JSON,改配置往往得重启本地服务。
- 协作成本高:新人接手项目,得教他配半天代理和证书。
前阵子我用 Go 写了一个不到 300 行代码的轻量工具,打包成单一二进制文件(体积十几兆,运行时内存占用才 8MB 左右),直接丢进项目根目录就能跑。今天把核心思路和关键代码拆出来聊聊。
1. 架构目标:我们要解决什么?
这个小工具主要承担三个职责:
- 自动 HTTPS:基于本地自签名根证书,无痛接管本地
HTTPS流量,浏览器不弹证书警告。 - 动态 Mock 拦截:如果本地
mocks/目录下有对应的 JSON 文件,直接返回 Mock 数据,支持热修改,无需重启。 - 透明反向代理:如果没有匹配到 Mock,把请求无缝转发给本地运行的 Vite / Webpack 服务(如
http://localhost:5173)或者远端联调网关。
整个数据流向非常清晰:
浏览器 (https://local.dev.com) -> Go Proxy (:443) -> [命中 Mock ? 返回本地 JSON : 转发至 Vite / 目标后端]
2. 核心实现步骤
第一步:解决证书与 HTTPS 监听
本地 HTTPS 最稳妥的方案是配合 mkcert 工具生成通用的本地根证书。假设我们生成了 local.dev.com.pem 和 local.dev.com-key.pem。
在 Go 里起一个 HTTPS 服务极为简单:
package main
import (
"log"
"net/http"
)
func startServer(handler http.Handler) {
certFile := "./certs/local.dev.com.pem"
keyFile := "./certs/local.dev.com-key.pem"
server := &http.Server{
Addr: ":443",
Handler: handler,
}
log.Println("Dev Proxy running at https://local.dev.com")
if err := server.ListenAndServeTLS(certFile, keyFile); err != nil {
log.Fatalf("HTTPS Server failed: %v", err)
}
}
第二步:Mock 文件的按需读取
我不喜欢把 Mock 逻辑写得太花哨,最直观的规则就是路径即文件。
比如前端请求 GET /api/v1/user/profile,我们就去检查本地项目里是否存在 ./mocks/GET/api/v1/user/profile.json。
因为是本地开发,直接在每次请求时读取磁盘文件,这样只要在编辑器里改了 JSON 内容,浏览器下一秒刷新立刻生效,完全不用重启进程。
package main
import (
"io"
"net/http"
"os"
"path/filepath"
"strings"
)
func tryServeMock(w http.ResponseWriter, r *http.Request) bool {
// 构建对应的本地文件路径
// 比如 mocks/GET/api/user.json
cleanPath := filepath.Clean(r.URL.Path)
mockPath := filepath.Join("mocks", r.Method, cleanPath+".json")
fileInfo, err := os.Stat(mockPath)
if err != nil || fileInfo.IsDir() {
return false // 文件不存在,跳过 Mock
}
data, err := os.ReadFile(mockPath)
if err != nil {
http.Error(w, "Failed to read mock file", http.StatusInternalServerError)
return true
}
// 设置标准的 JSON 响应头,并解决本地跨域问题
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "*")
w.WriteHeader(http.StatusOK)
w.Write(data)
return true
}
第三步:集成标准反向代理
当 Mock 文件不存在时,请求需要透传给本地的前端构建工具(例如 Vite)。Go 标准库自带的 httputil.ReverseProxy 性能非常顶,几行代码就能搞定:
package main
import (
"net/http"
"net/http/httputil"
"net/url"
)
type DevProxyHandler struct {
targetProxy *httputil.ReverseProxy
}
func NewDevProxyHandler(targetURL string) *DevProxyHandler {
target, _ := url.Parse(targetURL)
proxy := httputil.NewSingleHostReverseProxy(target)
// 自定义 Director 处理 Host 头,避免开发服务器 Host 校验报错
originalDirector := proxy.Director
proxy.Director = func(req *http.Request) {
originalDirector(req)
req.Host = target.Host
}
return &DevProxyHandler{
targetProxy: proxy,
}
}
func (h *DevProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 1. 处理预检请求 (CORS OPTIONS)
if r.Method == http.MethodOptions {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "*")
w.WriteHeader(http.StatusNoContent)
return
}
// 2. 尝试命中 Mock
if tryServeMock(w, r) {
return
}
// 3. 没命中,转发给本地 Vite 服务 (如 http://localhost:5173)
h.targetProxy.ServeHTTP(w, r)
}
第四步:组装入口
最后在 main.go 里把这几块拼起来:
package main
func main() {
// 目标开发服务器地址,也可以通过 flag 传入
viteServer := "http://localhost:5173"
handler := NewDevProxyHandler(viteServer)
startServer(handler)
}
3. 实际体验与收益
这套东西写完之后,我在我们团队的一个大型微前端项目里跑了几个月,几个感受很明显:
- 资源极度轻量:以前跑一套基于 Webpack Dev Server + Node 代理层,经常把风扇干转。现在 Go 编译出的单文件在后台跑着,CPU 占用基本是 0%,内存常驻不到 10MB。
- 前后端解耦联调:后端提供 API 文档后,前端直接在本地新建
mocks/POST/api/order/create.json,秒级开工。后端接口开发好了,直接删掉或者重命名这个 JSON 文件,请求自动无缝流向真实环境。 - 分发极其简单:不用管新人电脑上装的是 Node 16、18 还是 20,直接把编译好的二进制文件扔进公共仓库,配置一行
npm scripts:json"scripts": { "dev": "vite", "proxy": "sudo ./tools/dev-proxy" }
总结
前端开发做到后面,很多时候瓶颈都不在业务代码本身,而在周边工具链的体验上。
Go 的并发模型和网络标准库非常扎实,拿来写一些本地提效的小工具(比如小型的构建缓存分发、本地 HTTPS 网关、静态资源优化器)确实顺手。花小半天写这么个小轮子,换来每天开发环境的流畅响应,这笔买卖挺划算。
许可协议:CC BY-NC 4.0
更新于 11 小时前
觉得文章有帮助?点个赞吧!
0 条评论


