require 声明依赖模块路径与版本,是依赖解析基础;replace 仅在构建时重定向模块路径,不改变声明,二者必须共存——无 require 则 replace 无效,无 replace 则按默认方式拉取。
require 和 replace 的基本作用关系require 声明项目依赖的模块路径和版本,是构建时 Go 工具链解析依赖图的依据;replace 不改变依赖声明,只在构建阶段将某个模块路径映射到本地目录或另一仓库地址。两者必须共存才有意义——没有 require,replace 无从生效;只有 require 没有 replace,就走默认的代理或直接拉取远程 tag/commit。
常见错误现象:replace 写了但没效果,往往是因为对
应模块没出现在 require 列表里(包括间接依赖),或者路径拼写不一致(比如大小写、.git 后缀、v0/v1 版本号差异)。
require 必须显式存在,哪怕只是间接依赖,也建议用 go get -u 或手动补全后再加 replace
replace 的左边必须与 require 中声明的模块路径完全一致(包括末尾 /v2 这类语义化版本后缀)go.mod 文件,否则 go build 会报 no required module provides package
replace 指向未发布的模块典型场景:你正在同时开发 github.com/myorg/lib 和 github.com/myorg/app,后者依赖前者,但 lib 还没打 tag,也不希望推到远程测试。这时在 app/go.mod 里写:
require github.com/myorg/lib v0.0.0-00010101000000-000000000000 replace github.com/myorg/lib => ../lib
注意:require 版本不能留空或写 latest,Go 要求必须是合法伪版本(如上例)或具体 tag;../lib 是相对于当前 go.mod 文件的路径,不是 GOPATH 或 module root。
立即学习“go语言免费学习笔记(深入)”;
../lib 下没有 go.mod,运行 go mod init github.com/myorg/lib 初始化lib 后,app 不需要 go mod tidy 就能立即生效(因为 replace 是构建时重定向)replace 的 go.mod,可用 go mod edit -dropreplace=... 清理当你 fork 了一个开源库(比如 github.com/oldorg/tool),修复 bug 后想让自己的项目用这个 fork,但原项目仍被其他依赖间接引入,就会产生版本冲突。此时需同时处理直接和间接依赖:
先运行 go list -m all | grep oldorg/tool 确认它是否在依赖图中;再在 go.mod 中写:
require github.com/oldorg/tool v1.2.3 replace github.com/oldorg/tool => github.com/myfork/tool v1.2.4
关键点在于右边的 github.com/myfork/tool 必须已发布 tag(如 v1.2.4),且其 go.mod 中的 module 名仍是 github.com/oldorg/tool(否则导入路径不匹配)。
go.mod 改成了 github.com/myfork/tool,则必须同步改所有 import 语句,否则编译失败replace 可共存,但不能对同一模块路径写两条 replace,否则 go mod tidy 会报错go mod graph | grep tool 查看该模块是否被多个路径引入,必要时对每个路径单独 replace
replace 在 vendor 模式下的行为差异启用 go mod vendor 后,replace 依然生效,但 vendor 目录中存放的是替换后的代码(即 ../lib 或 github.com/myfork/tool 的内容),而不是原始 require 的模块。这意味着:
go mod vendor 前,确保所有 replace 目标可访问(比如本地路径存在、fork 仓库可 clone)replace 指向的内容强绑定,换环境时若缺失替换源,go build -mod=vendor 会失败replace,CI 构建时容易出错真正容易被忽略的是:一旦用了 replace,整个构建链路就脱离了 go.sum 的原始校验逻辑——Go 不再验证被替换模块的 checksum,而是信任你提供的来源。这在安全审计或合规场景中需要额外说明。