Yasin

Yasin

Go 开发热更新:告别手动重启,用 Air 实现自动重载

引言

写 Go 代码时,每次改一行都要 Ctrl+C 杀进程、再 go run main.go,这个循环极度打断心流。Node.js 有 nodemon,Python 有 watchdog,Go 生态对应的答案是 Air——一个专为 Go 设计的热重载工具,文件保存后自动重新编译并重启,整个过程不到一秒。

本文讲透 Air 的安装、配置、在 VS Code 中的集成,以及 Gin 框架下的完整使用方式。

核心工具:Air

安装

# 方式一:go install(推荐,自动安装到 GOPATH/bin)
go install github.com/air-verse/air@latest

# 方式二:使用官方安装脚本(Linux/macOS)
curl -sSfL https://raw.githubusercontent.com/air-verse/air/master/install.sh | sh -s -- -b $(go env GOPATH)/bin

安装后验证:

air -v
# 输出:  __    _   ___
#        / /\  | | | |_)
#       /_/--\ |_| |_| \  , built with Go ...

确保 $(go env GOPATH)/bin 在你的 PATH 中,否则 air 命令找不到。Windows 下通常是 C:\Users\你的用户名\go\bin

快速启动(零配置)

在项目根目录直接运行:

air

Air 会自动检测 .go 文件变化,重新编译并重启。效果如下:

  __    _   ___
 / /\  | | | |_)
/_/--\ |_| |_| \  , built with Go go1.22.0

watching .
watching handlers
!exclude tmp
building...
running...

修改并保存任意 .go 文件后,终端自动输出:

building...
running...

生成配置文件

air init

这会在项目根目录生成 .air.toml,可以精细控制行为:

# .air.toml

root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"

[build]
  # 编译命令
  cmd = "go build -o ./tmp/main ."
  # 编译产物路径
  bin = "tmp/main"
  # 需要监听的文件后缀
  include_ext = ["go", "tpl", "tmpl", "html"]
  # 排除的目录(node_modules、vendor 等不需要监听)
  exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"]
  # 排除特定文件名
  exclude_file = []
  # 排除以下划线开头的文件
  exclude_regex = ["_test\\.go"]
  # 文件修改后延迟多久重建(毫秒),防止连续保存触发多次构建
  delay = 1000
  # 停止旧进程的信号
  stop_on_error = true
  # 构建出错时是否继续运行旧版本
  rerun = false

[log]
  time = false
  main_only = false

[color]
  main = "magenta"
  watcher = "cyan"
  build = "yellow"
  runner = "green"

[misc]
  # 退出时是否清理 tmp 目录
  clean_on_exit = true

在 VS Code 中集成

方式一:直接在集成终端运行 Air(最简单)

打开 VS Code 内置终端(Ctrl+`),在项目根目录运行:

air

保持这个终端运行,正常在编辑器里写代码保存,Air 自动重载。这是最简单的方式,零配置。

方式二:配置 VS Code Task(推荐)

在项目根目录创建 .vscode/tasks.json,把 Air 设为一键启动任务:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Air: Hot Reload",
      "type": "shell",
      "command": "air",
      "isBackground": true,
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "presentation": {
        "reveal": "always",
        "panel": "dedicated"
      },
      "problemMatcher": []
    }
  ]
}

之后按 Ctrl+Shift+B 即可一键启动热重载,Air 在独立的终端面板中运行。

方式三:配合 VS Code 调试器(可断点 + 热重载)

Air 负责自动重建,VS Code Debugger 负责断点调试——两者可以结合。

.vscode/launch.json 中配置:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Go: Air Debug",
      "type": "go",
      "request": "attach",
      "mode": "remote",
      "remotePath": "${workspaceFolder}",
      "port": 2345,
      "host": "127.0.0.1"
    }
  ]
}

.air.toml 中把构建命令改为带 Delve 调试器的形式:

[build]
  cmd = "go build -gcflags='all=-N -l' -o ./tmp/main ."
  bin = "tmp/main"
  full_bin = "dlv exec --accept-multiclient --log --headless --continue --listen :2345 ./tmp/main"

启动 Air 后,在 VS Code 中 F5 附加调试器,即可在保持热重载的同时打断点。

注意:此模式每次重载后需要重新点 F5 附加调试器,适合需要深度调试的场景。日常开发直接用方式一/二即可。

Gin 框架下的完整示例

my-gin-app/
├── .air.toml
├── main.go
├── handlers/
│   └── user.go
└── tmp/          ← Air 自动创建,存放编译产物
// main.go
package main

import (
    "log"
    "my-gin-app/handlers"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.GET("/users", handlers.GetUsers)
    
    log.Println("Server running on :8080")
    r.Run(":8080")
}
// handlers/user.go
package handlers

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func GetUsers(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "users": []string{"alice", "bob"},
    })
}

运行 air 后,修改 handlers/user.go 返回的内容,保存,Air 自动重建,一秒内新代码生效。

常见问题

Air 命令找不到

# Windows PowerShell 中检查 GOPATH/bin 是否在 PATH
$env:GOPATH
# 输出如:C:\Users\YasinDoyle\go

# 手动添加到当前 session
$env:PATH += ";C:\Users\YasinDoyle\go\bin"

# 永久添加:在系统环境变量中把 %GOPATH%\bin 加入 PATH

端口已被占用(address already in use

Air 在某些情况下旧进程没有完全退出,新进程启动时端口冲突。解决方案:

# .air.toml - 增加停止延迟,等待旧进程释放端口
[build]
  delay = 1500  # 毫秒

或者在代码中使用 SO_REUSEPORT,但更简单的做法是增加 delay

只想监听特定目录

[build]
  include_dir = ["handlers", "models", "services"]  # 只监听这些目录

.env 文件支持

[build]
  # 启动命令前加载 .env 文件
  full_bin = "set -a && . ./.env && set +a && ./tmp/main"

Windows 下推荐配合 godotenv 在代码中加载:

import "github.com/joho/godotenv"

func main() {
    godotenv.Load() // 加载 .env
    // ...
}

关联知识

前置知识:

  • Go 模块系统(go.mod / go env GOPATH
  • 系统环境变量 PATH 的配置

延伸知识:

  • Delve:Go 专用调试器,与 Air 结合实现热重载 + 断点调试
  • Task:Go 版 Makefile,可替代 npm scripts 管理开发命令
  • Docker 开发环境中的 Air:在容器内运行 Air,配合 volume 挂载实现容器内热重载

参考文档:

实践建议

  1. tmp/ 加入 .gitignore:Air 的编译产物不需要提交:

    tmp/
    
  2. 团队统一配置:把 .air.toml 提交到 Git,确保所有人的开发体验一致。

  3. 最小验证:项目根目录直接 air,30 秒内就能感受热重载效果,不需要任何配置文件。

总结

在 VS Code 中写 Go 代码告别手动重启的最佳方案是 Airgo install github.com/air-verse/air@latest 安装后,在项目根目录直接运行 air,文件保存即自动重编译重启;进阶用法可通过 .air.toml 精细控制监听目录、编译参数,配合 VS Code Task 一键启动,或结合 Delve 调试器实现热重载 + 断点调试。