首页
/ Standard Go Project Layout 的 /vendor 目录详解:go mod vendor、-mod=vendor 标志与模块代理的取舍

Standard Go Project Layout 的 /vendor 目录详解:go mod vendor、-mod=vendor 标志与模块代理的取舍

2026-09-06 12:00:36作者:廉彬冶Miranda

本文基于 Standard Go Project Layout 仓库的 vendor/README.md,系统讲解 /vendor 目录在 Go 项目布局中的定位、如何用 go mod vendor 生成并维护依赖快照、-mod=vendor 构建标志在不同 Go 版本下的行为差异,以及模块代理(module proxy)在何种场景下可以完全替代 vendor 目录。读完本文,你能够对应用型项目与库项目做出正确的依赖管理决策,并在离线/内网环境中正确配置构建。

一、/vendor 目录在项目布局中的定位

vendor/README.md 用一句话定义了该目录的职责:/vendor 目录用于存放应用程序依赖,这些依赖可以手动管理,也可以由你喜欢的依赖管理工具、或者 Go 内置的 modules 特性来管理。

在整个项目布局的全景中,其他目录负责的是“组织自己的代码”:cmd/README.md 说明 /cmd 放各可执行程序的主入口、internal/README.md 说明 /internal 放不希望被外部导入的代码、pkg/README.md 说明 /pkg 放可供外部项目使用的库代码;而 /vendor 是其中职责完全不同的一个目录——它存放的不是你自己的代码,而是“外部依赖”的本地副本。这决定了它的维护方式与其他目录有本质区别:其他目录由开发者手工创建、随代码演进,/vendor 目录则通常由工具链批量生成,并且必须与 go.mod 保持同步。

从源码结构看,本模板仓库的 /vendor 目录下仅保留了 vendor/README.md 这一个说明文件,并没有任何实际依赖代码。可以推断,该模板只是预留了这个目录的位置与语义,是否真正启用 vendoring 模式完全交给项目自行决定。仓库根目录的 go.mod 声明了模块路径占位符(module github.com/YOUR-USER-OR-ORG-NAME/YOUR-REPO-NAME)与工具链版本(go 1.19),这是后文讨论构建行为的版本前提。

主文档 README.md 对布局模式的适用性还有一句重要的告诫:这些模式并没有被每一个项目使用,“甚至 vendor 模式也不是通用的”(even the vendor pattern is not universal)。因此下文的结论应当视为决策依据,而非强制性规范。

二、用 go mod vendor 生成 /vendor 目录

仓库主文档 README.md/vendor 小节给出了明确的操作入口:

The go mod vendor command will create the /vendor directory for you. (go mod vendor 命令会为你创建 /vendor 目录。)

这是创建 vendoring 目录的标准方式。作为 Go 工具链的内置行为,go mod vendor 具体做三件事:

  1. 依据 go.mod / go.sum 的依赖声明,找出构建与测试当前模块所需要的依赖模块;
  2. 把这些依赖模块的源代码拷贝进 vendor/ 目录,并按各模块自身的包结构组织;
  3. 生成 vendor/modules.txt 文件,记录每个被 vendored 模块的版本号与其包含的包列表。该文件是工具链判断 vendor 目录与 go.mod 是否一致的校验依据。

一个典型的新项目落地流程如下:

# 在 go.mod 中声明依赖后
go mod tidy        # 校正 go.mod / go.sum,剔除多余声明
go mod vendor      # 生成 /vendor 目录与 vendor/modules.txt
go build ./...     # 构建

日常维护时有两个要点:

  • vendor 目录是快照,不是真相源。 真相源始终是 go.modgo.sum。每次依赖发生变化(go get 升级、go mod tidy 清理等)之后,必须重新执行 go mod vendor,使 vendor/vendor/modules.txt 同步更新;否则工具链在构建时会检测到两者不一致并报错;
  • 只拷贝构建所需的依赖。 vendor/ 中只会包含构建与测试当前模块实际用到的包,因此目录体积通常明显小于完整的模块缓存。

三、有 vendor 目录时的构建行为:-mod=vendor 标志

README.md 中附有一段与版本密切相关的提示:

Note that you might need to add the -mod=vendor flag to your go build command if you are not using Go 1.14 where it's on by default. (如果你没有使用 Go 1.14——该版本中此行为默认开启——你可能需要在 go build 命令中添加 -mod=vendor 标志。)

也就是说,“vendor 目录存在时构建是否自动使用它”这一行为,以 Go 1.14 为分界线:

Go 版本 默认构建行为
1.14 之前 默认不会自动进入 vendor 模式;若要使用 vendor/,必须在 go build 上显式添加 -mod=vendor
1.14 及以后 vendor/modules.txt 存在、且与 go.mod 一致时,默认的 auto 模式自动进入 vendor 模式,直接从 vendor/ 读取依赖源码

工具链的 -mod 标志共有三个取值(Go 工具链行为):readonly(只读使用模块信息)、auto(默认;1.14+ 存在一致 vendor 目录时等价于 vendor 模式)、vendor(强制从 vendor/ 读取,目录缺失或不一致时直接报错)。

两点实操建议:

  • 对于决定提交 vendor/ 目录的项目,可以通过环境变量 GOFLAGS=-mod=vendor 全局启用该模式,避免团队成员或 CI 流水线中个别 go buildgo test 命令漏加标志;
  • 本仓库 go.mod 声明的是 go 1.19,因此基于该模板起步的项目只要生成了 vendor/ 并保持同步,构建会自动走 vendoring 模式,无需为每次命令手工添加标志。

四、关键规则:构建库时不要提交应用依赖

vendor/README.md 原文给出了一条明确的规则:

Don't commit your application dependencies if you are building a library. (如果你在构建一个库,不要把你的应用依赖提交进去。)

即:当你的项目是(会被其他项目以模块形式导入,对应本布局中 pkg/README.md 描述的对外公开库代码)时,不应提交 vendor/ 目录。从模块系统的工作机制可以推断其理由:

  • 使用方导入你的库时,Go 工具链会根据你 go.mod 中的版本声明自行解析全部传递依赖;提交 vendor/ 只会无谓增大仓库体积,其中内容属于使用方根本不需要的快照;
  • 依赖升级后,仓库里残留的旧 vendor/ 快照容易误导协作者,产生“go.mod 与 vendor 快照不一致”的冲突;
  • 文档本身也限定 /vendor 的语义是存放“应用程序依赖”(application dependencies),即面向最终产出可执行程序的应用场景;README.md 主文档同样强调 vendor 模式并非通用模式。

反过来,应用项目(对应 cmd/README.md 布局中产出二进制的工程,不会被外部导入)则往往受益于提交 vendor/:构建完全离线可复现、不依赖模块代理的可达性,适合内网 CI、隔离机器与供应链审计等场景。

五、模块代理:何时完全不需要 vendor 目录

vendor/README.md 在结尾给出了另一种取舍:自 Go 1.13 起,Go 同时启用了模块代理(module proxy)特性,默认使用 proxy.golang.org 作为模块代理服务器;只要该机制符合你的全部需求与约束,就完全不需要 vendor 目录

结合工具链行为,两种机制的分工可以概括为:

  • 模块代理:在线模式。依赖按需通过代理拉取并放入本地模块缓存,仓库里只保留 go.mod / go.sum,不提交一行依赖源码;代理地址可通过 GOPROXY 环境变量指向私有代理;
  • Vendoring:离线模式。依赖源码随仓库一起提交,构建时不连接任何代理;代价是仓库膨胀,且每次依赖变更后需要重新同步 vendor/

据此可以整理出一张选型速查表:

场景 建议做法
公网环境、默认代理可达 不启用 vendor,依赖模块代理即可(即原文“you won't need the vendor directory at all”的情形)
内网 / 离线 / 隔离环境构建 go mod vendor 生成并提交 vendor/;1.14 之前的 Go 版本构建时显式加 -mod=vendor
默认代理不可用,需私有代理 GOPROXY 指向私有代理;是否提交 vendor 可独立决策
库(被他人导入) 不提交 vendor 目录,把依赖解析交给使用方的工具链
应用(产出最终二进制) 可提交 vendor 目录,实现离线可复现构建

六、小结

  • 在 Standard Go Project Layout 中,/vendor 是专门存放应用依赖的目录,是布局里唯一的“依赖快照”目录;其生成与维护围绕 go mod vendor 命令和 vendor/modules.txt 校验文件展开;
  • 构建层面,Go 1.14 起在存在一致 vendor 目录时自动进入 vendor 模式,1.14 之前则需要手工为 go build 添加 -mod=vendor 标志,也可用 GOFLAGS=-mod=vendor 全局启用;
  • 原文档的两条硬规则值得牢记:构建库时不要提交应用依赖;模块代理(1.13 起,默认 proxy.golang.org)若满足你的需求与约束,则 vendor 目录完全不必存在;
  • 在本模板仓库中,/vendor 下仅含 vendor/README.mdgo.mod 声明 go 1.19 且模块路径为占位符;实际落地项目时,是否启用 vendoring 应结合自身的网络环境与项目类型(库或应用)来决定。
登录后查看全文
热门项目推荐
相关项目推荐