vendor目录是项目依赖的“本地快照”,编译时go build默认优先从中加载包,确保离线构建、版本可控与环境隔离;需配合go.mod/go.sum使用,并提交至Git以保障构建一致性。

Vendor目录是项目私有依赖的“本地镜像”
vendor 目录本质是把所有第三方包代码原样复制进项目根目录下的 vendor 文件夹,编译时 go build 会**优先从这里加载包**,跳过 GOPATH 和网络。它不记录版本号,只存代码快照——就像把图书馆整本书复印一份塞进你书桌抽屉,别人借不走、改不了,但你也无法知道这本《Go编程入门》到底是第2版还是第3版。
常见错误现象:go build 报错 cannot find package "github.com/sirupsen/logrus",但 vendor/ 下明明有这个目录?原因通常是 vendor/ 内部结构没对齐 import path(比如少了一级 github.com/sirupsen/logrus 目录),或 go.mod 存在且 GO111MODULE=on 时 vendor 被完全忽略。
- 必须手动执行
govendor init或go mod vendor生成,不是自动维护 - 提交到 Git 后体积暴涨,
vendor/下常有上千个文件 - 更新依赖靠人工比对、重新拉取,没有
go.sum那样的校验机制 - 仅适用于 Go 1.5–1.12 时期,Go 1.16+ 默认禁用 vendor 优先查找逻辑
Go Modules 的核心是声明式依赖 + 可验证快照
go mod 不复制源码,只在 go.mod 中声明依赖路径和版本(如 github.com/sirupsen/logrus v1.9.0),并在 go.sum 中记录每个模块的哈希值。构建时,go 命令按需下载模块到 $GOPATH/pkg/mod(全局缓存),再软链接或解压使用。它解决的不是“能不能编译”,而是“这次编译和上次是否用了完全一致的代码”。
典型使用场景:团队协作中某人 go get -u 升级了某个间接依赖,导致 CI 构建失败。Modules 通过 go.sum 锁死所有间接依赖哈希,让 go build 在任何机器上都复现相同行为。
立即学习“go语言免费学习笔记(深入)”;
-
GO111MODULE=on是前提,否则go.mod文件会被忽略 -
go mod tidy自动补全缺失依赖、清理未用项,go mod graph查依赖环 - 模块路径(如
example.com/myapp)不再强制要求放在$GOPATH/src下 - 升级依赖用
go get github.com/sirupsen/logrus@v1.13.0,不是改 vendor 目录
为什么 go mod vendor 不等于“回到 Vendor 模式”
命令 go mod vendor 是 Modules 生态下的一个兼容性出口,它把当前 go.mod 解析出的所有依赖,按路径结构展开到 vendor/ 目录——但它**仍依赖 go.mod 和 go.sum**。删除 go.mod 后,这个 vendor/ 目录就失去意义;反之,即使有 vendor/,只要 GO111MODULE=on,go build 仍默认走模块缓存,除非显式加 -mod=vendor 参数。
容易踩的坑:go mod vendor 后直接删掉 go.sum,再执行 go build -mod=vendor 会失败,因为 vendor 目录本身不含哈希校验信息,校验仍靠 go.sum 驱动。
-
go build -mod=vendor才真正启用 vendor 目录,否则vendor/只是冗余文件 -
go mod vendor生成的vendor/modules.txt是内部记录,不可替代go.mod - CI 环境若设
GO111MODULE=off,即使有vendor/也会报no Go files错误
学习时最易混淆的两个点
一是把 vendor/ 当成“Modules 的子集”或“降级方案”——其实它是不同设计哲学的产物:Vendor 是“复制即确定”,Modules 是“声明即确定”。二是认为 go mod vendor 能绕过代理或网络——它只是把已缓存的模块内容摊平,首次 go mod download 仍需联网拉取。
真正需要 vendor 的场景极少:比如离线构建且不允许缓存共享(如某些军工系统 CI),或遗留项目过渡期。日常开发中,go mod + go.sum + GOPROXY 组合已足够健壮。坚持手管 vendor/ 目录,反而容易因忘记同步 go.mod 导致行为不一致。


















