Go 1.17 新特性:Module 有哪些變化?
閱讀本文大概需要 5 分鐘。
大家好,我是 polarisxu。
自從 Go1.11 增加 Go Module 以來,每個(gè)版本都在不斷改進(jìn) Module。Go1.17 也不例外。這次最主要的變化有兩點(diǎn):
Module graph pruning:Module 依賴圖修剪 Lazy Loading:Module 延遲加載
此外還有 Deprecated 注釋等。本文就一起探究下這些新變化,因?yàn)橛腥藳]看懂,不知道這些變化是什么意思。
01 Module 依賴圖修剪
要搞懂這個(gè)知識(shí)點(diǎn),需要對(duì)比 1.17 之前的情況。
注意,這個(gè)變化并不會(huì)影響 go mod 的任何使用
為了方便演示,我們構(gòu)建一個(gè)這樣的例子。
有四個(gè)模塊:主模塊(studymod)和 a、b、c 三個(gè)模塊,如下圖:

module studymod 是我們的項(xiàng)目,它依賴模塊 a 中的 x 包,而 x 包依賴模塊 b,同時(shí) a 包中的 y 包依賴模塊 c。
很顯然,對(duì)我們的項(xiàng)目 studymod 來說,模塊 c 的代碼根本沒用上。Go 1.17 對(duì) module 的改進(jìn)主要就是在這種沒用上的模塊上。
基于 Go1.16
假定在 $HOME 下創(chuàng)建一個(gè)新目錄 gomod116,構(gòu)建如下目錄結(jié)構(gòu):
[xuxinhua@/Users/xuxinhua/gomod116]
$ tree -L 3
.
├── a
│ ├── x
│ │ └── x.go // 包含正確的包定義和 import
│ └── y
│ └── y.go
├── b
│ └── b.go
├── c
│ └── c.go
└── studymod.go
在 Go 文件中只是簡(jiǎn)單的定義包和 import。幾個(gè) go 文件的內(nèi)容分別如下:
// x.go
package x
import _ "b"
// y.go
package y
import _ "c"
// b.go
package b
// c.go
package c
// studymod.go
package studymod
import _ "a/x"
進(jìn)入 gomod116 目錄,將 Go 版本切換到 Go1.16.x(多版本發(fā)揮作用了,多版本問題,可以看看這篇文章:終于找到了一款我喜歡的安裝和管理 Go 版本的工具),然后執(zhí)行下列命令:
$ go mod init studymod
$ cd a
$ go mod init a
$ cd ../b
$ go mod init b
$ cd ../c
$ go mod init c
因?yàn)槟K a 依賴模塊 b 和 c,往 a/go.mod 增加如下代碼:
module a
go 1.16
require (
b v0.1.0
c v0.1.0
)
在 gomod116 根目錄的 go.mod 增加如下代碼:
module studymod
go 1.16
require a v0.1.0
replace (
a v0.1.0 => ./a
b v0.1.0 => ./b
c v0.1.0 => ./c
)
因?yàn)?studymod 只直接依賴模塊 a。replace 部分可以忽略,只是為了本地能夠正常引入模塊。
此時(shí),在 gomod116 目錄下執(zhí)行 go build,如果不報(bào)錯(cuò),表示一切正常,我們可以執(zhí)行如下命令看到依賴關(guān)系:
$ go mod graph
studymod [email protected]
[email protected] [email protected]
[email protected] [email protected]
studymod 依賴 a,a 依賴 b 和 c。
但我們知道,studymod 模塊實(shí)際根本不需要模塊 c,因此,我們嘗試在 studymod 模塊中刪除模塊 c 的引用,即刪除 go.mod 中 replace 部分的 c v0.1.0 => ./c,再次執(zhí)行 go build:
$ go build
go: [email protected] requires
[email protected]: missing go.sum entry; to add it:
go mod download c
可見模塊 c 不能少。(驗(yàn)證后,記得將 go.mod 恢復(fù)原樣)
基于 Go1.17
現(xiàn)在我們基于 Go1.17 做類似的驗(yàn)證,將 Go 切到 1.17,執(zhí)行如下命令,將 gomod116 拷貝一份:
$ cp -rf gomod116 gomod117
然后進(jìn)入 gomod117 目錄,將 go.mod 的 版本由 1.16 改為 1.17:
module studymodgo 1.17require a v0.1.0replace ( a v0.1.0 => ./a b v0.1.0 => ./b c v0.1.0 => ./c)
接著執(zhí)行 go mod tidy,發(fā)現(xiàn) go.mod 變成這樣:
module studymodgo 1.17require a v0.1.0require b v0.1.0 // indirectreplace ( a v0.1.0 => ./a b v0.1.0 => ./b c v0.1.0 => ./c)
多了一行 require,記錄了 module studymod 的間接依賴:module [email protected]。執(zhí)行 go build 一切正常。
跟 Go1.16 一樣,刪除掉 c v0.1.0 => ./c 這行,再次執(zhí)行 go build,依然正常。這就是依賴圖裁剪,再看依賴關(guān)系,跟 Go1.16 是不一樣的。
$ go mod graphstudymod [email protected] [email protected]@v0.1.0 [email protected]@v0.1.0 [email protected]
這么做的優(yōu)劣
這么做是基于社區(qū)的反饋,有興趣的可以看看這個(gè)提案:Proposal: Lazy Module Loading,這也是合理的,畢竟沒有用到的代碼為什么一定需要呢?
此外,裁剪后,go.mod 中會(huì)包含入更多的依賴項(xiàng)(完整的依賴列表),新包含的依賴項(xiàng)單獨(dú)放在一個(gè) require 下。可以通過 https://github.com/studygolang/studygolang 試驗(yàn)下。下載該代碼后,執(zhí)行如下命令:
$ go mod tidy -go=1.17
diff 后,在原來的基礎(chǔ)上多出了如下的 require:

有點(diǎn)類似其他語言中 xxx.lock 文件的感覺了。
當(dāng)然,這種方式后,go.mod 的文件會(huì)更大,這也是該方式的一個(gè)相對(duì)劣勢(shì)。
02 延遲加載(lazy Loading)
理解了上面依賴圖的裁剪,延遲加載就一句話:那些根本沒有用上的模塊(比如上面例子中的模塊 c),Go 1.17 后,Go 命令不會(huì)去讀取其 go.mod 文件。如果之后需要了,再去加載。
03 Deprecated 注釋
Go1.17 go.mod 中支持 Deprecated 注釋,用來標(biāo)明該模塊廢棄了。
// Deprecated: use example.com/mod/v2 instead.module example.com/mod
對(duì)于那些使用了被廢棄的 module 的 go 項(xiàng)目,go list、go get 命令都會(huì)給出 warning。
看過我這篇文章:《從 go-chi 框架撤回所有主版本聊 Go1.16 的新特性》 的朋友,可能會(huì)有疑問,撤回和廢棄有何不同,如何分別使用?這里稍微總結(jié)下:
撤回的使用場(chǎng)景:
發(fā)現(xiàn)了一個(gè)嚴(yán)重的安全漏洞; 發(fā)現(xiàn)了嚴(yán)重的不兼容性或 bug; 這個(gè)版本是偶然發(fā)布的,或是過早發(fā)布了;
而 Deprecated 是用來作廢整個(gè) module 的,也就是說,不能廢棄某個(gè) minor 或 patch 版本。Deprecated 更多用來提示使用者升級(jí)到更新的 major 版本,比如要廢棄 v1,希望大家升級(jí)到 v2,就應(yīng)該使用 Deprecated。
04 總結(jié)
還有其他一些小的變動(dòng),這里不一一列舉。提醒下大家,從 Go1.16 版本開始,下載安裝 Go 二進(jìn)制程序,不再使用 go get,而是 go install,go get 只用來下載普通包。
因?yàn)?module 越來越完善,官方針對(duì) module 也有更完整的文檔:https://docs.studygolang.com/ref/mod。
我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗(yàn)!2012 年接觸 Go 語言并創(chuàng)建了 Go 語言中文網(wǎng)!著有《Go語言編程之旅》、開源圖書《Go語言標(biāo)準(zhǔn)庫》等。
堅(jiān)持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場(chǎng)心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長(zhǎng)!也歡迎加我微信好友交流:gopherstudio
