air默认只监听.go文件,改config.yaml或templates/*.html不重启是设计使然;需在go.mod根目录运行,配置root="."、watch和build中均设置include_ext=["go","yaml","html"],且build.bin与build.cmd输出路径严格一致。

air 能热更新,但默认只响应 .go 文件改动;改了 config.yaml、templates/*.html 或 assets/js/app.js,它压根不重启——这不是 bug,是设计如此。
为什么改了代码 air 却没反应?
最常见原因不是工具坏了,而是它根本没监控到你改的文件。
- 必须在
go.mod所在目录运行air,不是main.go所在目录(比如你在cmd/app/下执行,就失效) -
air.toml中的root字段如果写成绝对路径(如/home/user/myapp),换机器或用户后会静默失败;留空或设为"."更可靠 - 默认不递归扫描符号链接、
vendor/、.git/,也不进internal/或pkg/子模块——如果你的 handler 在internal/handler/里,又没配include_dir,那改了也没用
build.bin 和 build.cmd 必须严格一致
这是启动失败的高频雷区:air 启动时会尝试执行 build.bin 指向的文件,如果它和 build.cmd 实际输出的路径不一致,就会报 exec: "./tmp/main": file does not exist。
-
build.cmd写的是"go build -o ./tmp/main .",那build.bin就得是"./tmp/main"(不能是"tmp/main",也不能漏掉./) - Linux/macOS 上常遇到
fork/exec ./tmp/main: permission denied:因为go build输出的二进制可能没执行位(尤其 NFS 挂载或某些 CI 环境)。解决办法是把build.cmd改成:"go build -o ./tmp/main . && chmod +x ./tmp/main" - Windows 下别直接覆盖
main.exe,建议加时间戳后缀,例如:"go build -o ./tmp/main_%time:~0,2%%time:~3,2%%time:~6,2%.exe ."(PowerShell 中可用Get-Date -UFormat %s替代)
要监听模板或配置文件,两处 include_ext 都得改
air 的 watch 和 build 是两个独立阶段:watch 决定“什么变化触发构建”,build 决定“构建后是否重启”。漏配任意一处,都会导致“改了文件但无反应”。
- 在
[watch]节加:include_ext = ["go", "yaml", "yml", "html", "tmpl", "json"] - 在
[build]节也加同样列表,否则即使文件被检测到,构建逻辑也不会触发 - 加太多后缀有代价:Linux 默认 inotify 句柄上限是 8192,项目大了容易报
inotify limit reached;宁可按需加,别一股脑堆["*"]
改了非 Go 文件,真有必要靠 air 重启吗?
对纯文案、CSS、HTML 模板这类不涉及 Go 运行时逻辑的改动,用 air 杀进程重启反而是重的。HTTP 连接中断、状态丢失、数据库连接池重建……这些开销其实可以避免。
- 更轻量的做法是让程序自己监听文件:用
fsnotify加载templates/或config.yaml,改完自动 reload,不重启进程 - air 的定位始终是 Go 代码热重载,不是全栈 Live Reload;把它当“编译+启停代理”用,而不是万能热加载器
- 如果非要走 air,记得设
build.delay = 1000(单位毫秒),防止快速连保存导致文件还没落盘就触发构建
真正卡住人的,往往不是不会配,而是误以为 air 应该“自动感知所有改动”。它只做一件事:盯紧你告诉它的路径和后缀,然后跑命令。其余的,得靠你对项目结构和 Go 构建模型的理解来兜底。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











