必须显式使用 -mod=vendor 才能强制 Go 编译器从 vendor/ 目录加载依赖;仅执行 go mod vendor 不会自动启用 vendor 模式,否则仍可能回退到模块缓存或网络拉取。

编译时用 -mod=vendor 强制走本地 vendor 目录
当你把依赖复制到 vendor/ 后,go build 默认不会自动启用 vendor 模式(Go 1.14+ 才默认优先读 vendor)。必须显式加参数才能生效:
-
go build -mod=vendor:强制从vendor/加载所有依赖,完全跳过$GOPATH/pkg/mod和网络拉取 - 不加
-mod=vendor时,即使存在vendor/,Go 仍可能回退到模块缓存——尤其当go.mod里有新 require 但 vendor 未同步时 -
go build -mod=readonly是另一条路:禁止任何自动修改go.mod或下载行为,但依赖仍来自模块缓存,不是 vendor
常见错误是只运行了 go mod vendor 就以为万事大吉,结果编译时还是去拉网上的版本。vendor 不是“开关”,是“路径”,得靠 -mod=vendor 告诉编译器“请走这条路”。
用 replace 在构建时替换模块源码路径
replace 是真正动态调整依赖来源的机制,它不改版本号,只改代码来源。适用于调试本地 fork、离线构建、或 patch 第三方库。
- 在
go.mod中写:replace github.com/sirupsen/logrus => ./local-logrus,然后go build就会用./local-logrus下的代码,而非远程仓库 - 命令行临时替换(不写入
go.mod):go build -mod=readonly -modfile= ./local-logrus"),适合 CI 或一次性验证 -
replace仅影响构建时解析,go list -m all输出仍显示原始模块路径和版本——别靠这个判断实际用了哪份代码
注意:如果 replace 指向的是未初始化的 Git 仓库(比如刚 git clone 下来但没 go mod init),go build 会报 no Go files in directory,得先确保被替换目录里有合法的 go.mod 或至少一个 .go 文件且声明了正确 package。
立即学习“go语言免费学习笔记(深入)”;
go build 不会自动更新 go.mod 中的 require 版本
很多人误以为改完 go.mod 里的 require github.com/gin-gonic/gin v1.9.0 再 go build 就能切到 v1.9.0——其实不会。Go 不会因为 go.mod 改了就去下载新版本。
- 真正触发版本切换的只有两个动作:
go get github.com/gin-gonic/gin@v1.9.0,或go mod tidy(前提是 import 语句已引用该模块) - 手动改
require行后忘记运行go mod download,会导致go build报错:missing go.sum entry for module providing package - 如果模块已被缓存但版本不对,
go mod download -dirty可强制重新下载指定模块(慎用,会忽略校验)
本质是:Go 的模块解析分两步——先读 go.mod 构建初始图,再根据 MVS 算法从本地缓存中选版本。你改的只是“声明”,不是“事实”。
跨版本兼容性要靠 //go:build 而不是 go.mod 的 go 行
go.mod 开头的 go 1.22 只控制工具链行为(如 go vet 规则),不影响运行时特性可用性。真正决定某段代码是否参与编译的,是 //go:build 指令。
- 想让一段代码只在 Go 1.24+ 编译:
//go:build go1.24,并确保文件以.go结尾(不能是.go124这类后缀) - 多个条件用空格连接:
//go:build go1.22 && !go1.23表示仅限 Go 1.22.x -
go build -gcflags="-d=checkptr=0"这类底层 flag 不影响依赖选择,只改编译器行为;它和replace或-mod是正交的
最容易被忽略的是:哪怕你本地装的是 Go 1.24,只要代码里写了 //go:build go1.25,那段代码就永远不进编译流程——跟 go.mod 里写的 go 1.22 完全无关。


















