Go的编译器是按目录(Directory)来管理代码的,当写下 import "github.com/shinerio/my-awesome-project/pkg/validator" 时,Go 会去做两件事:
- 找到这个目录下所有的
.go文件。 - 把这些文件里的代码全部合并在一起看作一个整体,一起加载
1. 典型目录结构
my-awesome-project/
├── cmd/ # 程序的入口(main 函数所在处)
│ ├── app-server/ # 项目的主服务器程序
│ │ └── main.go
│ └── app-cli/ # 如果有命令行工具,放在这里
│ └── main.go
├── internal/ # 私有应用代码(重点:此目录下的包无法被外部项目导入)
│ ├── auth/ # 认证逻辑
│ │ ├── service.go
│ │ └── repository.go
│ ├── config/ # 配置加载
│ │ └── config.go
│ └── db/ # 数据库连接初始化
│ └── mysql.go
├── pkg/ # 可以被外部项目引用的公共库代码
│ ├── logger/ # 自定义的日志封装
│ └── validator/ # 通用的验证器
├── api/ # API 定义文件(如 Swagger, Proto 文件)
│ └── openapi.yaml
├── configs/ # 配置文件(如 yaml, json, env)
│ └── config.yaml
├── deployments/ # 部署相关(Docker, Kubernetes, Terraform)
│ └── Dockerfile
├── scripts/ # 编译、安装、分析用的脚本
│ └── build.sh
├── test/ # 额外的外部测试数据或集成测试
├── go.mod # 模块依赖定义
├── go.sum # 依赖版本校验和
└── README.md
2. vendor
在Go Modules出现之前,依赖管理是一片“西部世界”。vendor目录的出现,本质上是为了实现依赖的本地化隔离。简单来说,vendor 目录就是一个存放项目所有外部依赖包副本的文件夹。
2.1. vendor 的核心作用
当你运行 go mod vendor 命令时,Go 会把 go.mod 中记录的所有第三方库下载下来,并全部塞进项目根目录下的 vendor 文件夹里。
它的主要意义在于:
- 离线构建: 所有的代码都在本地。即使公司内网断了,或者GitHub挂了,你依然可以编译项目。
- 版本锁死(确定性): 即使原作者在 GitHub 上删除了库,或者由于某些原因版本找不到了,你的
vendor里依然有一份拷贝,保证项目永远能跑起来。 - 无需重复下载: 在 CI/CD(持续集成)环境中,不需要每次都去远程下载几百个包,直接从本地读取,速度极快。
2.2. Go 如何对待 vendor?
Go 的编译器有一套查找依赖的优先级逻辑:
- 开启 Vendor 模式: 如果你使用了
-mod=vendor参数(或者在 Go 1.14+ 版本中,如果根目录存在vendor目录且go.mod版本号 $\ge 1.14$),Go 会优先从vendor目录里找包。 - 默认模式(Module Cache): 如果没有
vendor目录,Go 会去系统的全局缓存目录($GOPATH/pkg/mod)里找。
2.3. 现在还有必要用 vendor 吗?
自从 Go Modules(2018年以后)成熟以来,关于是否保留 vendor 有两派观点:
2.3.1. 应该使用 vendor 的场景:
- 企业内网开发: 无法访问外网,或者访问 GitHub 极慢。
- 极端稳定性要求: 防止依赖库被作者“投毒”或物理删除。
- 单体仓库大型项目: 方便在没有网络的环境下快速分发完整的源码包。
2.3.2. 不建议使用 vendor 的场景:
- 开源库: 如果你在写一个库给别人用,千万不要带上
vendor,这会导致引用你的用户遇到包冲突。 - 个人/普通项目: 使用 Go Proxy(如
goproxy.cn)已经足够快且稳定,没必要在 Git 里多存几万行别人的代码,让仓库变得臃肿。
2.4. 常用命令
| 命令 | 作用 |
|---|---|
go mod vendor |
根据 go.mod 创建或更新 vendor 目录。 |
go build -mod=vendor |
强制要求编译器只从 vendor 目录读取代码,不联网。 |
go mod tidy |
清理没用的依赖,但它不会自动同步 vendor,之后通常要补一个 go mod vendor。 |
2.5. 避坑指南
如果你决定使用 vendor,记得把整个 vendor 目录提交到 Git。如果不提交,这个目录就失去了“本地副本”的意义。
评论