结论:go mod vendor 不是打包成单文件,而是将 go.mod 中 require 的直接和间接依赖的 go 源码复制到 vendor/ 目录,供离线构建使用;真正生成可执行文件需配合 go build -mod=vendor。

直接说结论:go mod vendor 不是“打包成一个文件”,而是把所有依赖源码复制进 vendor/ 目录,为离线构建做准备;真正生成可执行文件,仍需配合 go build -mod=vendor。
go mod vendor 生成的 vendor 目录到底包含什么
它只包含 go.mod 中 require 声明的直接和间接依赖的 Go 源码(.go 文件),不含二进制、C 头文件、嵌入资源或 go:embed 引用的文件。如果你的项目用了 //go:embed assets/ 或调用了 CGO,vendor 不会自动带这些内容——得手动复制。
常见错误现象:go build -mod=vendor 报错 pattern assets/**: no matching files 或 undefined reference to XXX(CGO 符号未定义),就是漏了非 Go 源码部分。
-
vendor/下的包结构与模块路径一致,例如github.com/gin-gonic/gin@v1.9.0会解压到vendor/github.com/gin-gonic/gin/ - 不会包含
replace或exclude规则里被跳过的模块 -
go.sum不参与 vendor,但构建时仍会校验 vendor 内文件哈希(除非显式关掉-mod=readonly)
什么时候必须重新运行 go mod vendor
每次 go.mod 变更后,vendor/ 不会自动同步。不重跑就构建,大概率失败。
典型场景包括:
- 执行
go get github.com/some/pkg@v2.1.0后 - 手动编辑
go.mod增删require行 - 运行
go mod tidy清理或补全依赖后(这步本身不改 vendor) - CI/CD 流水线中,即使没改代码,只要依赖有更新风险,建议加
go mod vendor --vendored-only防止意外拉取网络版本
注意:go mod vendor 默认会覆盖整个 vendor/ 目录,不增量更新。如果目录里混入了你手动加的补丁文件,会被清掉。
go build -mod=vendor 构建失败的常见原因
这个参数强制 Go 忽略 go.mod 的远程声明,只从 vendor/ 读依赖。但它对一致性要求极严。
容易踩的坑:
-
vendor/缺少某个间接依赖:比如 A 依赖 B,B 依赖 C,但 C 没出现在go.mod require里(标记为// indirect),go mod vendor仍会把它拉进来;但如果之前执行过go mod edit -droprequire或误删,就会丢 -
go.mod和vendor/版本不匹配:例如go.mod写着v1.8.0,但vendor/github.com/xxx/里是v1.7.5的源码(可能因缓存或手动改过) - 本地
replace没同步到 vendor:比如你在go.mod里写了replace example.com/pkg => ./local-pkg,go mod vendor会跳过它,但构建时又找不到原模块 → 必须确保replace指向的是已存在的本地路径,且该路径不在vendor/内 - Go 版本不兼容:vendor 目录里的某些包用了高版本语法(如泛型),而构建机上 Go 版本太低,报
syntax error—— 这和 vendor 无关,但常被误判
vendor 目录要不要提交到 Git
没有绝对答案,取决于团队对构建确定性和仓库体积的权衡。
提交的适用场景:
- CI/CD 环境完全离线,或网络策略禁止构建时访问代理
- 交付物需“一键编译”,客户不希望额外装 Go 或配代理
- 审计要求:所有依赖源码必须与发布版本 100% 对应,不可依赖外部服务可用性
不提交的适用场景:
- 依赖体积大(如含大量测试/示例代码),导致 clone 缓慢
- 团队信任
go.sum校验 + 缓存机制,且 CI 有稳定网络 - 使用 Go Workspaces 管理多模块,vendor 放在 workspace 根目录更合理
真正容易被忽略的一点:无论提不提交 vendor/,都必须确保 go.mod 和 go.sum 是最新且受控的——它们才是依赖事实的唯一权威来源。vendor 只是它的快照。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











