学完这篇你能得到:在自己的 Go 项目里搭好「改代码保存就自动重编译重启」的开发环境,并且知道 air 和 reflex 各自适合什么场景,不用再手动 Ctrl+C 再 go run。
第一步:把工具装到能直接调用的位置
air 和 reflex 都是 Go 写的,用 go install 装:
go install github.com/air-verse/air@latest
go install github.com/cespare/reflex@latest
装完先确认能敲出来:
air -v
reflex --version
注意:go install 的产物落在 $(go env GOPATH)/bin,这个目录默认不在 PATH 里。报 command not found 的话先执行 export PATH=$PATH:$(go env GOPATH)/bin,并写进 ~/.zshrc 或 ~/.bashrc。
第二步:用 air 起一个项目
进到项目根目录(有 go.mod 的那一层),执行:
air init
它会生成一份 .air.toml。如果版本较老没有 init 子命令,手动新建同名文件即可,然后直接 air 启动。最小可用配置长这样:
root = "."
tmp_dir = "tmp"
[build]
cmd = "go build -o ./tmp/main ./cmd/app"
bin = "./tmp/main"
include_ext = ["go", "tpl", "tmpl", "html"]
exclude_dir = ["assets", "tmp", "vendor", "node_modules", ".git"]
delay = 500
stop_on_error = true
send_interrupt = true
kill_delay = "2s"
[log]
time = true
几个关键项的作用:
cmd:编译命令,输出到 tmp_dir 里
bin:编译产物路径,air 负责运行它
include_ext:监听哪些后缀,Web 项目别漏了模板后缀
delay:文件变动后等多久再触发,防止编辑器保存多个文件时连打好几次
stop_on_error:编译失败时保留旧进程还是停掉,建议 true,错误一眼看得见
注意:exclude_dir 里一定要排除 tmp_dir 和 node_modules。编译产物如果落在被监听的目录里,会出现「编译 → 触发变动 → 再编译」的无限重启。
第三步:几个必须按项目改的点
入口不是 main.go 在根目录:改 cmd 和 bin 成你的实际路径,比如 go build -o ./tmp/main ./cmd/server。
用 Docker 或虚拟机挂载目录:容器里的 inotify 常常收不到宿主机的文件事件,在 [build] 里加 poll = true,改成轮询模式才有效。
要优雅退出:服务里有连接池、要关 DB 的话,send_interrupt = true 配合 kill_delay = "2s",让旧进程收到信号后有时间收尾,而不是被直接杀掉。
编译 + 前端一起跑:cmd 可以写复合命令,例如 go build -o ./tmp/main ./cmd/app && npm run build(Windows 下把 && 换成用 cmd /c 包一层)。
第四步:reflex —— 不用写配置文件的那条路
reflex 本质是个通用文件监听器,不绑定 Go,一行命令就能跑:
reflex -r '\.go$' -s -- sh -c 'go build -o ./tmp/app ./cmd/app && ./tmp/app'
参数含义:
-r '\.go$':监听匹配这个正则的文件
-s:启动时先执行一次,不用等你改代码
-R 'vendor|tmp':反向排除,不需要监听的目录
-g '*.html':按 glob 匹配,和 -r 二选一或叠加
--:后面跟真正要执行的命令
想把参数固定下来,写进配置文件用 -c 加载:
reflex -c reflex.conf
reflex.conf 里每行一组参数,# 开头是注释。
注意:reflex 不要直接 -- go run ./cmd/app。reflex 杀的是它启动的那个进程,而 go run 会再 fork 一个真正的二进制,容易留下占着端口的孤儿进程,下次启动就报 address already in use。用 go build 再执行,或者套一层 sh -c。
第五步:怎么选
- 项目是纯 Go、想零思考直接跑 → air。配置全在一个 toml 里,输出带时间戳和颜色,日志干净,团队里复制一份
.air.toml 就统一了。
- 项目有前端资源、模板、proto,要按不同文件类型跑不同命令 → reflex。它不关心你是什么语言,正则和 glob 更灵活,也能同时监听多个目录。
- 两个都装也不冲突,日常用 air,临时排查用 reflex 单条命令盯某个目录。
额外提一句:热重载只适合本地开发。上了测试/生产环境还是老实 go build 出二进制跑,热重载会带来额外的编译开销和文件监听负担。
小结
go install 后记得把 $(go env GOPATH)/bin 加进 PATH。
- air 用
air init 生成 .air.toml,重点改 cmd、bin、include_ext、exclude_dir。
- 排除编译产物目录,否则无限重启;容器里加
poll = true。
- reflex 用
-r 正则 + -s 即刻启动 + -R 排除,命令放在 -- 后面。
- reflex 别直接跟
go run,先 build 再执行,避免残留进程占端口。
- 热重载属于本地开发工具,不要带进生产。