最早的時候,Go 語言所依賴的所有的第三方庫都放在 GOPATH 這個目錄下面,這就導致了同一個庫只能保存一個版本的代碼。如果不同的項目依賴同一個第三方的庫的不同版本,應該怎麼解決?
go module 是 Go 語言從 1.11 版本之後官方推出的版本管理工具,並且從 Go1.13 版本開始,go module 成為了 Go 語言默認的依賴管理工具。
常用的go mod
命令如下表所示:
命令 | 作用 |
---|---|
go mod download | 下載依賴包到本地 (默認為 GOPATH/pkg/mod 目錄) |
go mod edit | 編輯 go.mod 文件 |
go mod graph | 打印模塊依賴圖 |
go mod init | 初始化當前文件夾,創建 go.mod 文件 |
go mod tidy | 增加缺少的包,刪除無用的包 |
go mod vendor | 將依賴複製到 vendor 目錄下 |
go mod verify | 依賴校驗 |
go mod why | 解釋為什麼需要依賴 |
GOPROXY#
proxy 顧名思義就是代理伺服器的意思。大家都知道,國內的網路有防火牆的存在,這導致有些 Go 語言的第三方包我們無法直接通過go get
命令獲取。GOPROXY 是 Go 語言官方提供的一種通過中間代理商來為用戶提供包下載服務的方式。要使用 GOPROXY 只需要設置環境變量 GOPROXY 即可。
目前公開的代理伺服器的地址有:
- goproxy.io;
- goproxy.cn:(推薦)由國內的七牛雲提供。
MacOS 或 Linux 下設置 GOPROXY 的命令為:
export GOPROXY=https://goproxy.cn
Go 語言在 1.13 版本之後 GOPROXY 默認值為 https://proxy.golang.org 在國內可能會存在下載慢或者無法訪問的情況,所以十分建議大家將 GOPROXY 設置為國內的 goproxy.cn。
使用 go get 命令下載指定版本的依賴包#
執行go get
命令,在下載依賴包的同時還可以指定依賴包的版本。
- 運行
go get -u
命令會將項目中的包升級到最新的次要版本或者修訂版本; - 運行
go get -u=patch
命令會將項目中的包升級到最新的修訂版本; - 運行
go get [包名]@[版本號]
命令會下載對應包的指定版本或者將對應包升級到指定的版本。
提示:go get [包名]@[版本號]
命令中版本號可以是 x.y.z 的形式,例如 go get foo@v1.2.3,也可以是 git 上的分支或 tag,例如 go get foo@master,還可以是 git 提交時的哈希值,例如 go get foo@e3702bed2。
如何在項目中使用#
【示例 1】創建一個新項目:
- 在 GOPATH 目錄之外新建一個目錄,並使用
go mod init
初始化生成 go.mod 文件。
go mod init hello
go: creating new go.mod: module hello
go.mod 文件一旦創建後,它的內容將會被 go toolchain 全面掌控,go toolchain 會在各類命令執行時,比如go get
、go build
、go mod
等修改和維護 go.mod 文件。
go.mod 提供了 module、require、replace 和 exclude 四個命令:
- module 語句指定包的名字(路徑);
- require 語句指定的依賴項模塊;
- replace 語句可以替換依賴項模塊;
- exclude 語句可以忽略依賴項模塊。
初始化生成的 go.mod 文件如下所示:
module hello
go 1.13
- 添加依賴。
新建一個 main.go 文件,寫入以下代碼:
1. package main
3. import (
4. "net/http"
5. "github.com/labstack/echo"
6. )
8. func main() {
9. e := echo.New()
10. e.GET("/", func(c echo.Context) error {
11. return c.String(http.StatusOK, "Hello, World!")
12. })
13. e.Logger.Fatal(e.Start(":1323"))
14. }
執行go run main.go
運行代碼會發現 go mod 會自動查找依賴自動下載:
go run main.go
go: finding github.com/labstack/echo v3.3.10+incompatible
go: downloading github.com/labstack/echo v3.3.10+incompatible
go: extracting github.com/labstack/echo v3.3.10+incompatible
go: finding github.com/labstack/gommon v0.3.0
......
go: finding golang.org/x/text v0.3.0
____ __
/ __/___/ / ___
/ _// __/ _ \/ _ \
/___/\__/_//_/\___/ v3.3.10-dev
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
O\
⇨ http server started on [::]:1323
現在查看 go.mod 內容:
module hello
go 1.13
require (
github.com/labstack/echo v3.3.10+incompatible // indirect
github.com/labstack/gommon v0.3.0 // indirect
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect
)
go module 安裝 package 的原則是先拉取最新的 release tag,若無 tag 則拉取最新的 commit,詳見 Modules 官方介紹。
go 會自動生成一個 go.sum 文件來記錄 dependency tree:
github.com/davecgh/go-spew v1.1.0/go.mod h1/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/labstack/echo v3.3.10+incompatible h1=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.3.0 h1=
github.com/labstack/gommon v0.3.0/go.mod h1+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
... 省略很多行
再次執行腳本go run main.go
發現跳過了檢查並安裝依賴的步驟。
可以使用命令go list -m -u all
來檢查可以升級的 package,使用go get -u need-upgrade-package
升級後會將新的依賴版本更新到 go.mod * 也可以使用go get -u
升級所有依賴。
【示例 2】改造現有項目。
項目目錄結構為:
├─ main.go
│
└─ api
└─ apis.go
main.go 源碼為:
1. package main
3. import (
4. api "./api" // 這裡使用的是相對路徑
5. "github.com/labstack/echo"
6. )
8. func main() {
9. e := echo.New()
10. e.GET("/", api.HelloWorld)
11. e.Logger.Fatal(e.Start(":1323"))
12. }
api/apis.go 源碼為:
1. package api
3. import (
4. "net/http"
6. "github.com/labstack/echo"
7. )
9. func HelloWorld(c echo.Context) error {
10. return c.JSON(http.StatusOK, "hello world")
11. }
- 使用
go mod init ***
初始化 go.mod。
go mod init hello
go: creating new go.mod: module hello
- 运行
go run main.go
。
go run main.go
go: finding golang.org/x/crypto latest
build _/D_/code/src/api: cannot find module for path _/D_/code/src/api
首先還是會查找並下载安装依賴,然後運行腳本 main.go,這裡會拋出一個錯誤:
build /D/code/src/api: cannot find module for path /D/code/src/api
但是 go.mod 已經更新:
module hello
go 1.13
require (
github.com/labstack/echo v3.3.10+incompatible // indirect
github.com/labstack/gommon v0.3.0 // indirect
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect
)
那為什麼會拋出這個錯誤呢?
這是因為 main.go 中使用 internal package 的方法跟以前已經不同了,由於 go.mod 會掃描同工作目錄下所有 package 並且變更引入方法,必須將 hello 當成路徑的前綴,也就是需要寫成 import hello/api,以往 GOPATH/dep 模式允許的 import ./api 已經失效。
- 更新舊的 package import 方式。
所以 main.go 需要改寫成:
1. package main
3. import (
4. api "hello/api" // 這裡使用的是相對路徑
5. "github.com/labstack/echo"
6. )
8. func main() {
9. e := echo.New()
10. e.GET("/", api.HelloWorld)
11. e.Logger.Fatal(e.Start(":1323"))
12. }
提示:在 Go 語言 1.11 版本下使用 go mod 時可能會遇到 go build github.com/valyala/fasttemplate: module requires go 1.12 這種錯誤,遇到類似這種需要升級到 1.12 的問題,直接升級到 Go 語言 1.12 版本以上就好了。
- 到這裡就和新創建一個項目沒什麼區別了。
goimoprts 能做什麼#
Goimports 可以自動將依賴分成標準庫和非標準庫兩個組並將組內的依賴進行排序。
goimports 有什麼缺陷#
Goimports 最大的問題是沒法處理空行,一旦依賴之間通過空行隔開了,那麼 goimports 會把兩個空行之間的代碼作為一個獨立分組,此時 import 代碼塊內整體上的分組和排序就無法保證了。