写 Go 时,很多人最容易被两类小问题打断:一类是忘了删掉没用的包,结果编译直接失败;另一类是代码里已经用了某个包,却还得回头手动补 import。表面上只是几秒钟的整理动作,但在日常开发、多人协作和提交检查里,这些重复劳动会不断累积。与其在 gofmt 之后继续手修 import,不如直接把 goimports 放进日常流程里。下面就按“它和 gofmt 到底差在哪、怎么在本地用起来、怎么接进提交和 CI”这几个问题,梳理一套更适合项目发布和团队协作的用法。
gofmt 和 goimports 到底差在哪
很多 Go 开发者都熟悉 gofmt。它是 Go 官方提供的格式化工具,主要负责代码排版,比如缩进、括号位置,以及同一个 import 块内部的字母排序。

goimports 可以理解为 gofmt 的增强版,或者更直接一点说,是一个覆盖面更完整的日常格式化工具。它不仅会做常规格式化,还会自动管理 import 语句的增删、合并和分组。
| 功能特性 | gofmt | goimports |
|---|---|---|
| 代码格式化 | ✓ | ✓ |
| import块内按字母排序 | ✓ | ✓ |
| 按标准库/第三方库分组 | ✗ | ✓ |
| 合并多个import声明 | ✗ | ✓ |
| 自动添加缺失的import | ✗ | ✓ |
| 自动删除未使用的import | ✗ | ✓ |
先看一个最常见的例子。假设代码如下:
package main
import "os"
import "fmt"
func main() {
fmt.Println("hello")
}
这段代码里,os 没有被使用,而且 import 还是拆开的两个声明。
运行 gofmt 后,结果保持不变,因为它不会主动合并独立的 import 声明,也不会删除未使用的包:
package main
import "os"
import "fmt"
func main() {
fmt.Println("hello")
}
如果运行的是 goimports,它会做三件事:合并 import、按字母排序、删除未使用的 os:
package main
import "fmt"
func main() {
fmt.Println("hello")
}
再看分组的区别。下面这段代码同时包含标准库和第三方包:
package main
import (
"github.com/sirupsen/logrus"
"fmt"
"os"
)
func main() {
fmt.Println("hello")
logrus.Info("test")
os.Exit(0)
}
gofmt 只会按字母顺序整理,不会区分包来源:
import (
"fmt"
"github.com/sirupsen/logrus"
"os"
)
goimports 则会把标准库和第三方库分开,中间保留空行,阅读起来更清楚:
import (
"fmt"
"os"
"github.com/sirupsen/logrus"
)
更关键的一点是,如果你已经在代码里使用了某个包,但漏写了对应的 import,goimports 还能自动补上。这也是很多团队最终把它作为默认格式化方案的原因:它保留了 gofmt 的排版能力,同时把 import 管理也一起接管了,而且几乎不增加额外使用成本。
命令行怎么用,适合什么场景
如果你还没有安装 goimports,可以先执行:
go install golang.org/x/tools/cmd/goimports@latest
针对单个文件,最直接的用法是:
goimports -w yourfile.go
这里的 -w 表示直接写回文件。如果不带这个参数,通常更适合做查看或比对;带上 -w 则更符合日常开发习惯,执行后文件会被就地修正。
如果想一次处理项目里的多个 Go 文件,可以批量运行:
find . -name "*.go" -exec goimports -w {} ;
这种方式适合几个典型场景:
- 第一次把旧项目切换到
goimports; - 从别的编辑器迁移过来,想先统一一遍仓库;
- CI 检查失败后,需要本地快速修复整批文件。
不过,命令行手动执行更像是补救手段,而不是最佳实践。因为只要依赖“记得运行一次”,就还是会有人遗漏。更稳妥的做法,是把它接入编辑器、提交钩子和 CI,让格式化变成自动动作。
编辑器里如何自动执行 goimports
对大多数开发者来说,最省事的接入方式就是“保存时自动运行”。这样写完代码按一次保存,格式和 import 都会同时整理。
VS Code
VS Code 用户需要先安装 Go 扩展,然后在 settings.json 中配置:

{
"[go]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "golang.org/x/tools/gopls"
},
"gopls": {
"formatting.gofumpt": false
}
}
这里有一个容易混淆的点:gopls 默认使用的是 gofmt。如果你希望获得 goimports 那套 import 管理能力,还需要在 gopls 相关设置里启用对应选项,或者直接在 Go 扩展设置中选择格式化工具。也就是说,只打开 formatOnSave 还不够,关键是确认保存动作最后调用的是哪套格式化能力。
GoLand
GoLand 可以通过 Tools -> File Watchers 添加 watcher,把文件保存动作和 goimports 关联起来:
Program: goimports
Arguments: -w $FilePath$
这种方式的好处是改动范围明确,保存当前文件时就会触发处理,不需要每次手动开终端执行命令。
Vim
如果你使用 Vim,可以在 .vimrc 中加入:
let g:go_fmt_command = "goimports"
let g:go_fmt_autosave = 1
这样保存文件时就会自动调用 goimports。对于键盘流工作方式很重的用户来说,这通常是最低干扰、也最容易长期坚持的方案。
无论你使用哪种编辑器,核心原则都一样:尽量把格式化前置到“写代码的当下”,而不是留到提交前再集中补救。越早自动处理,越不容易把 import 问题带进后续环节。
提交前和 CI 里怎么兜底
编辑器自动格式化解决的是个人开发效率问题,但团队协作还需要兜底机制。最常见的两层保障分别是 Git 钩子和 CI/CD 检查。
pre-commit 钩子
通过 pre-commit,可以在提交前自动格式化暂存区里的 Go 文件:
#!/bin/sh
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
[ -z "$gofiles" ] && exit 0
goimports -w $gofiles
git add $gofiles
这段脚本的逻辑很直接:
- 先找出当前已暂存、并且后缀为
.go的文件; - 如果没有 Go 文件,就直接退出;
- 如果有,就执行
goimports -w; - 最后重新
git add,把格式化后的结果放回暂存区。
它的价值在于,即便有人本地编辑器没有正确配置,也能在真正提交前把明显的格式问题拦下来。
GitHub Actions 检查
在持续集成里加入检查,是面向仓库质量的最后一道防线。以 GitHub Actions 为例,可以直接使用下面的步骤:
- name: Check formatting
run: |
go install golang.org/x/tools/cmd/goimports@latest
if [ -n "$(goimports -l .)" ]; then
echo "Code is not formatted"
exit 1
fi
这里的思路不是自动改文件,而是用 goimports -l . 列出所有未按规范整理的内容。只要输出不为空,CI 就报错并终止流程。这样做能保证进入主分支的代码至少在格式和 import 管理上保持一致。
Makefile 集成
如果项目本身通过 Makefile 管理常用命令,也可以把格式化和检查单独封装出来:
.PHONY: fmt check-fmt
fmt:
goimports -w .
check-fmt:
@if [ -n "$(shell goimports -l .)" ]; then
echo "Code is not formatted. Run 'make fmt'";
exit 1;
fi
这样团队里就会形成统一入口:本地修复执行 make fmt,流水线检查执行 make check-fmt。命令统一以后,文档、脚本和 CI 配置也更容易同步。
实际项目里该怎么选
如果只回答“gofmt 还是 goimports”,结论其实很明确:在绝大多数项目里,优先用 goimports 更合适。
原因并不复杂。gofmt 解决的是排版一致性,而 Go 开发里的高频摩擦点,很多都集中在 import 管理上。未使用的包、漏导入的依赖、多个 import 声明未合并、标准库和第三方库混排,这些问题单看都不大,但它们恰好是最容易重复出现、也最适合交给工具处理的一类问题。
goimports 的价值就在这里:它不是替代 Go 代码风格,而是在 gofmt 的基础上,把开发者最常手动收尾的那部分一起自动化。对于个人项目,它能减少保存后的修修补补;对于团队项目,它能降低代码风格争议,并减少因 import 问题导致的编译失败或无意义提交。
更实际的落地顺序可以是这样:
- 先安装
goimports并在编辑器里启用保存自动整理; - 再根据团队习惯增加
pre-commit钩子,降低漏网概率; - 最后在 CI/CD 中加入检查,把规范真正变成门槛。
做到这三步之后,goimports 基本就不再是一个“偶尔手动执行的工具”,而会变成 Go 项目默认的一部分。对大多数团队来说,这比单独使用 gofmt 更完整,也更接近日常开发的真实需求。













