Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Go入门go.mod 文件

Go新手入门之一篇文章彻底讲清楚go.mod文件

作者:一颗青果

在Golang中,go.mod文件记录了项目的依赖模块及其版本信息,每个依赖模块都有一个唯一的哈希值(h1),用于确保模块的完整性和一致性,这篇文章主要介绍了Go新手入门之一篇文章彻底讲清楚go.mod文件的相关资料,需要的朋友可以参考下

学 Go 的时候,很多人第一眼看到 go.mod 会有点懵:

module example.com/package-demo

go 1.26

这两行看起来很短,但它们决定了几个非常重要的问题:

一句话概括:

go.mod 是 Go module 的配置文件,它声明模块名、Go 版本、依赖版本和依赖替换规则。

它不是普通的注释文件,也不是给编辑器看的说明书。go 命令会读取它、更新它,并根据它决定怎么编译、测试和下载依赖。

先看一个最小 go.mod

一个最小的 go.mod 通常长这样:

module example.com/package-demo

go 1.26

第一行:

module example.com/package-demo

表示当前模块的模块路径是:

example.com/package-demo

第二行:

go 1.26

表示这个模块按 Go 1.26 的语言和工具链规则来处理。

如果你的项目暂时没有外部依赖,go.mod 里可能就只有这两行。

module、package、go.mod 的关系

新手最容易把三个概念混在一起:

module
package
go.mod

它们不是一个东西。

概念

作用

例子

module

一组一起发布、一起管理版本的 Go 包

example.com/package-demo

package

一个目录下共同编译的一组 Go 文件

greetings

go.mod

描述当前 module 的配置文件

module example.com/package-demo

假设目录结构是:

package-demo/
  go.mod
  main.go
  greetings/
    english.go
    chinese.go

go.mod

module example.com/package-demo

go 1.26

那么 greetings 包的导入路径就是:

import "example.com/package-demo/greetings"

公式是:

包的导入路径 = module 路径 + 从 go.mod 所在目录开始算的子目录

所以:

package-demo/greetings
=> example.com/package-demo/greetings

这就是 go.mod 和包导入路径之间最核心的关系。

go.mod 放在哪里

go.mod 应该放在模块根目录。

比如:

myapp/
  go.mod
  main.go
  user/
    user.go
  order/
    order.go

这个 myapp/ 就是模块根目录。

从这个目录往下的包,都属于同一个 module:

myapp
myapp/user
myapp/order

如果你在子目录里又创建了一个新的 go.mod,那它会变成另一个独立 module。新手阶段不建议随便嵌套多个 go.mod,除非你明确知道自己要做多模块仓库。

怎么创建 go.mod

使用:

go mod init 模块名

例如:

go mod init example.com/myapp

会生成:

module example.com/myapp

go 1.26

模块名可以是未来的仓库地址:

go mod init github.com/alice/shop

也可以是本地练习时的名字:

go mod init myapp

不过如果以后要发布给别人用,推荐一开始就使用仓库路径,例如:

module github.com/alice/shop

这样别人就可以用清晰稳定的路径导入你的包。

go.mod 的基本语法

go.mod 是按行解析的文本文件。

常见格式是:

关键字 参数...

例如:

module example.com/myapp
go 1.26
require github.com/gin-gonic/gin v1.10.0

多个同类指令可以写成块:

require (
	github.com/gin-gonic/gin v1.10.0
	golang.org/x/text v0.16.0
)

可以写行注释:

require golang.org/x/sys v0.22.0 // indirect

注意:go.mod 支持 // 注释,不支持 /* ... */ 这种块注释。

module指令

module 用来声明当前模块的模块路径。

语法:

module module-path

示例:

module example.com/package-demo

它有两个重要作用。

第一,它是当前模块的名字。

第二,它是当前模块中所有包的导入路径前缀。

例如:

module example.com/package-demo

目录:

greetings/
  english.go

导入路径:

import "example.com/package-demo/greetings"

v2 及以上版本的特殊规则

如果你的模块发布到了 v2 或更高主版本,模块路径通常要带主版本后缀:

module example.com/mylib/v2

别人导入时也要写:

import "example.com/mylib/v2/client"

新手写普通项目时大多不用马上关心这个规则,但如果以后发布公共库,这是 Go module 版本管理里很重要的一点。

go指令

go 指令声明当前模块要求的最低 Go 版本,以及 Go 工具应该按哪个版本的规则处理这个模块。

语法:

go minimum-go-version

示例:

go 1.26

它不是注释,也不是随便写的版本号。

它会影响:

从 Go 1.21 开始,go 指令表示使用该模块所需的最低 Go 版本。如果工具链太旧,遇到声明了更高 Go 版本的模块,会拒绝使用它。

比如:

go 1.26

意味着这个模块要求 Go 1.26 或更高版本来使用。

require指令

require 用来声明依赖。

require module-path module-version

示例:

require github.com/gin-gonic/gin v1.10.0

意思是:当前模块依赖 github.com/gin-gonic/gin,版本至少选到 v1.10.0

多个依赖通常写成:

require (
	github.com/gin-gonic/gin v1.10.0
	golang.org/x/text v0.16.0
)

注意:require 后面写的是模块路径,不一定等于你代码里 import 的包路径。

例如代码里可能写:

import "golang.org/x/text/cases"

go.mod 里写的是:

require golang.org/x/text v0.16.0

原因是 casesgolang.org/x/text 这个模块里的一个包。

// indirect是什么意思

你经常会看到:

require golang.org/x/sys v0.22.0 // indirect

// indirect 表示间接依赖。

简单说:

你的代码直接 import 的模块:直接依赖
你的依赖又依赖的模块:间接依赖

例如你的代码直接用了 A

你的项目 -> A -> B

A 是直接依赖,B 是间接依赖。

go.mod 里可能表现为:

require A v1.2.3
require B v0.9.0 // indirect

新手不要手动乱删 // indirect。更推荐让 Go 自己整理:

go mod tidy

它会自动添加缺失的依赖,也会删除不再需要的依赖。

go.mod 和 go.sum 的区别

很多新手会问:有了 go.mod,为什么还要 go.sum

它们职责不同。

文件

作用

要不要提交

go.mod

声明模块路径、Go 版本、依赖需求和替换规则

go.sum

记录依赖内容的校验和,防止下载到被篡改的模块

go.mod 像依赖清单:

我需要 gin v1.10.0

go.sum 像校验记录:

我下载到的 gin v1.10.0 应该长这样

一般不要手动编辑 go.sum。让 Go 命令维护它:

go mod tidy

replace指令

replace 用来替换依赖来源。

最常见场景是本地调试。

假设你的项目依赖:

require example.com/mylib v1.2.3

但你正在本地修改 mylib,目录结构是:

workspace/
  myapp/
    go.mod
  mylib/
    go.mod

可以在 myapp/go.mod 里写:

require example.com/mylib v1.2.3

replace example.com/mylib => ../mylib

这样 Go 不会去远程下载 example.com/mylib,而是直接使用本地的 ../mylib

replace 不需要改 import

即使加了:

replace example.com/mylib => ../mylib

代码里的导入路径仍然写原来的:

import "example.com/mylib/client"

不要改成:

import "../mylib/client"

Go module 模式下,代码里的 import 仍然应该写模块路径。replace 只是告诉 Go:解析这个模块路径时,实际去另一个地方找代码。

replace 单独写不一定生效

一个常见错误是只写:

replace example.com/mylib => ../mylib

但没有任何地方 require example.com/mylib

replace 本身不会自动把模块加入依赖图。通常需要配合 require

require example.com/mylib v0.0.0

replace example.com/mylib => ../mylib

如果只是本地临时调试,v0.0.0 可以作为一个占位版本。

exclude指令

exclude 用来排除某个模块版本。

语法:

exclude module-path module-version

示例:

exclude example.com/badlib v1.3.0

意思是:不要在当前模块的依赖解析里使用 example.com/badlibv1.3.0 版本。

这个指令不常用。通常只有当某个版本有严重问题,比如校验异常、版本损坏、不可用时,才会考虑使用。

对于新手来说,知道它的存在即可。日常开发中更常用的是 requirereplacego mod tidy

retract指令

retract 是给模块发布者用的。

语法:

retract version // reason
retract [version-low, version-high] // reason

示例:

retract v1.1.0 // Published accidentally.

意思是:当前模块的 v1.1.0 版本不建议别人再依赖。

再比如:

retract [v1.0.0, v1.0.5] // Build broken on some platforms.

表示撤回一段版本。

注意:retract 不是删除版本。已经依赖这个版本的人通常还能构建,只是 Go 会提示这个版本不推荐使用。

如果你只是写业务项目或本地练习项目,基本不用写 retract

toolchain指令

toolchain 用来建议当前模块使用某个 Go 工具链。

示例:

toolchain go1.26.3

它和 go 指令不同。

go 1.26

表示最低 Go 版本和语言规则。

toolchain go1.26.3

表示建议使用的具体 Go 工具链版本。

新手阶段可以先不手写它。很多时候让 Go 工具自动管理即可。

godebug指令

godebug 用来设置当前模块里 main 包和测试二进制的默认 GODEBUG 行为。

示例:

godebug panicnil=1

它主要用于 Go 版本升级时的兼容行为控制。新手日常写业务代码时很少需要它。

先记住:

godebug 是高级兼容设置,不是管理普通依赖的指令。

tool指令

tool 用来把某个命令行工具声明为当前模块的工具依赖。

示例:

tool golang.org/x/tools/cmd/stringer

配合 require

tool golang.org/x/tools/cmd/stringer

require golang.org/x/tools v0.24.0

之后可以在模块目录内运行:

go tool stringer

这个适合管理代码生成器、项目工具等。新手可以先知道它是“项目工具依赖”的写法,不必一开始就用。

ignore指令

ignore 用来告诉 Go 在匹配包路径时忽略某些目录。

示例:

ignore ./node_modules

也可以写成块:

ignore (
	static
	content/html
	./third_party/javascript
)

它主要影响类似下面这种命令:

go test ./...
go list ./...

如果项目里有大量非 Go 目录、生成目录或前端目录,ignore 可以避免 Go 在递归扫描时把它们当成包目录处理。

一个更完整的 go.mod 示例

下面是一个偏真实项目的例子:

module github.com/alice/shop

go 1.26

toolchain go1.26.3

require (
	github.com/gin-gonic/gin v1.10.0
	gorm.io/gorm v1.25.12
)

require (
	golang.org/x/crypto v0.25.0 // indirect
	golang.org/x/net v0.27.0 // indirect
)

replace example.com/local/payment => ../payment

ignore ./node_modules

可以这样读:

module github.com/alice/shop

当前模块叫 github.com/alice/shop

go 1.26

要求 Go 1.26 或更高版本。

toolchain go1.26.3

建议使用 Go 1.26.3 工具链。

require github.com/gin-gonic/gin v1.10.0

依赖 Gin。

// indirect

说明是间接依赖。

replace example.com/local/payment => ../payment

本地调试时,把远程模块替换成本地目录。

ignore ./node_modules

递归匹配包时忽略前端依赖目录。

常用命令

初始化模块

go mod init github.com/alice/shop

创建 go.mod

添加依赖

go get github.com/gin-gonic/gin

添加或升级依赖。

指定版本:

go get github.com/gin-gonic/gin@v1.10.0

升级到最新补丁或小版本:

go get -u github.com/gin-gonic/gin

整理依赖

go mod tidy

这是最常用命令之一。

它会:

写 Go 项目时,经常在提交代码前跑一次:

go mod tidy

查看模块依赖

go list -m all

查看当前模块最终使用到的所有模块。

查看某个依赖为什么被引入:

go mod why golang.org/x/sys

查看依赖图:

go mod graph

本地替换依赖

go mod edit -replace example.com/mylib=../mylib

删除替换:

go mod edit -dropreplace example.com/mylib

新手最常见的错误

1. 在没有 go.mod 的目录里运行项目

错误表现可能是:

go: go.mod file not found

解决:

go mod init your-module-name

或者进入真正的模块根目录再运行:

cd your-project-root
go run .

2. module 名和 import 路径对不上

go.mod

module myapp

代码却写:

import "example.com/myapp/user"

这样就对不上。

如果 module 是:

module myapp

那内部包应该导入:

import "myapp/user"

如果想写:

import "example.com/myapp/user"

go.mod 就应该是:

module example.com/myapp

3. 把包路径写成文件路径

错误:

import "example.com/myapp/user/user.go"

正确:

import "example.com/myapp/user"

Go 导入的是包,也就是目录,不是具体 .go 文件。

4. 手动乱改 go.sum

go.sum 不是给人手写的依赖清单。

如果依赖乱了,先试:

go mod tidy

如果还有问题,再看具体报错。

5. replace 到本地目录,但本地目录没有 go.mod

例如:

replace example.com/mylib => ../mylib

那么 ../mylib 通常应该是一个模块根目录,里面应该有自己的:

../mylib/go.mod

否则 Go 可能无法把它当作一个完整模块解析。

6. 以为 go.mod 是锁文件

go.mod 不是传统意义上的锁文件。

它记录依赖需求和版本选择结果,但真正用于校验模块内容的是 go.sum

因此团队协作时通常两个文件都要提交:

go.mod
go.sum

7. 发布 v2 模块却忘了路径加/v2

如果一个公共库发布到了 v2,模块路径通常要写:

module example.com/mylib/v2

使用者导入:

import "example.com/mylib/v2/client"

这和很多语言的依赖管理习惯不同,是 Go module 的重要规则。

新手应该怎么记

刚开始不用把所有指令都背下来。

优先掌握这几个:

module
go
require
replace

它们覆盖了大多数日常开发场景。

可以这样记:

module:我是谁
go:我要求什么 Go 版本
require:我依赖谁
replace:临时把依赖换到哪里

其他指令先了解:

exclude:排除某个坏版本
retract:发布者撤回自己发错的版本
toolchain:建议使用的 Go 工具链
godebug:兼容性调试开关
tool:项目工具依赖
ignore:包扫描时忽略目录

推荐工作流

写一个新 Go 项目时,可以按这个顺序:

mkdir myapp
cd myapp
go mod init github.com/yourname/myapp

写代码:

touch main.go

需要第三方库时:

go get github.com/gin-gonic/gin

运行或测试:

go run .
go test ./...

提交前整理依赖:

go mod tidy

提交:

go.mod
go.sum
你的 .go 文件

一句话总结

go.mod 是 Go 项目的模块说明书。

它最核心的作用是:

声明当前模块是谁
声明当前模块需要哪个 Go 版本
声明当前模块依赖哪些模块和版本
声明依赖解析时有哪些替换、排除、工具和忽略规则

新手只要先掌握:

module 决定导入路径前缀
go 决定最低 Go 版本和工具行为
require 记录依赖
replace 用于本地调试或临时替换
go mod tidy 负责整理依赖

就已经能应付绝大多数 Go 项目了。

参考资料

到此这篇关于Go新手入门之go.mod文件的文章就介绍到这了,更多相关Go入门go.mod 文件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文