必须显式使用 -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 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 完全无关。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











