目录结构

Go的编译器是按目录(Directory)来管理代码的,当写下 import "github.com/shinerio/my-awesome-project/pkg/validator" 时,Go 会去做两件事:

  1. 找到这个目录下所有.go 文件。
  2. 把这些文件里的代码全部合并在一起看作一个整体,一起加载

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 的编译器有一套查找依赖的优先级逻辑:

  1. 开启 Vendor 模式: 如果你使用了 -mod=vendor 参数(或者在 Go 1.14+ 版本中,如果根目录存在 vendor 目录且 go.mod 版本号 $\ge 1.14$),Go 会优先从 vendor 目录里找包。
  2. 默认模式(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。如果不提交,这个目录就失去了“本地副本”的意义。

评论