Go 語言的模組化
前言
在很久很久以前,就 push 自己學過 go 語言,但是之前只是看了一下基礎語法就放棄了,實在是工作當中沒有應用場景。最近發現基於 go 寫的 esbuild 異軍突起,想要深入研究下它的奧祕,發現看不懂。於是,打算先從 go 開始學一遍,等我把 go 學好了,再去研究 esbuild。所以,最近的幾篇文章都會寫 go 的一些學習心得,今天的文章就從 go 語言的模組化開始。

環境變數
學習 go 語言的第一步,當然是安裝以及環境變數。由於我是 macos,直接執行 brew install go
就能安裝成功,也可以在官網(http://golang.google.cn/)下載對應的二進位制包。
安裝成功後,需要配置下面幾個環境變數:
-
GOROOT:go 語言的安裝路徑; -
GOBIN:go 語言的可執行檔案路徑,一般為 "$GOROOT/bin"
; -
GOPATH:工作目錄,可設定多個,每個專案都可以設定一個單獨的GOPATH;
GOPATH
在 GoLand(go 語言最強IDE) 中,我們可以在 Preferences
中設定多個 GOPATH,而且將 GOPATH 分為全域性和區域性的。

GOPATH 最早出現的意義是用來進行模組管理,每個 GOPATH 中會有三個目錄:
-
src:用來存放原始碼; -
pkg:用來存放編譯後的 .a(archive)
靜態庫檔案; -
bin:用來存放編譯後可直接執行的二進位制檔案;

一般設定為工作目錄的 src 資料夾需要手動建立,其他兩個目錄都是編譯後自動生成的。
接下來,我們新建了一個目錄 ~/Code/goland/go-story
,並將該目錄設定為工作目錄。
export GOPATH="~/Code/goland/go-story"
然後在當前目錄新建一個 src
資料夾,並新建一個 hello
目錄,在 hello
目錄新建 main.go
檔案。

在 hello/main.go
檔案中,寫入如下程式碼:
package main
import (
"flag"
"fmt"
)
var name string
func init() {
flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func main() {
flag.Parse() // 解析命令列引數
fmt.Printf("\nHello %s\n", name)
}
flag 庫是 go 內建的模組,類似於 node 的 commander 庫,執行後結果如下所示:

下面我們引入一個能夠讓命令列輸出色彩更加豐富的庫:colourize,類似於 node 中的 chalk。通過下面這個命令來安裝依賴:
go get github.com/TreyBastian/colourize
執行之後,我們可以看到在工作區自動建立了一個 pkg
目錄,目錄下新生成的是 colourize
庫檔案,同時 src 目錄也新建了一個 github.com
目錄,用來放 colourize
的原始碼。

go get
命令可以簡單理解為 npm install
。接下來就能在 hello/main.go
中引入依賴。
package main
import (
"flag"
"fmt"
"github.com/TreyBastian/colourize"
)
var name string
func init() {
flag.StringVar(&name, "name", "everyone", "The greeting object.")
}
func hello(name string) {
fmt.Printf(colourize.Colourize("\nHello %s\n", colourize.Blue), name)
}
func main() {
flag.Parse()
hello(name)
}
執行 hello/main.go
可以看到命令列輸出了藍色的文字。

預設情況下,go 依賴的載入機制為:
-
$GOROOT
下的src
目錄 -
$GOPATH
下的src
目錄
Go Vendor
前面這種方式,有個很麻煩的問題,就是沒有辦法進行很好的版本管理,而且多個依賴分散在 $GOPATH/src
目錄下,可能會出現很多很麻煩的問題。
例如,我現在在 GOPATH
下有兩個專案:go-blog
、go-stroy
,這兩個專案分別有不同的依賴,分散在 github.com
目錄,這個時候到底要不要將整個 github.com
目錄新增到版本庫呢?

go 在 1.5 版本的時候,引入了 vendor 機制,在每個專案目錄下可以通過 vendor
目錄存放依賴,這類似於 node 中的 node_modules
目錄。

使用 go vendor
需要先安裝 govendor
模組。
go get govendor
然後在專案目錄執行如下命令。
cd ~/Code/gland/go-story/src/hello
govendor init
govendor add github.com/TreyBastian/colourize
可以看到,hello
專案下新生成了一個 vendor
目錄,而且 colourize
也被拷貝到了該目錄下。

而且 govendor
會新建一個 vendor.json
檔案,用來進行依賴項的管理。

有了 go vendor
之後,依賴項的載入順序如下:
-
專案目錄下的 vendor
目錄 -
專案目錄上一級的 vendor
目錄 -
不斷向上冒泡 ……(PS. 類似於 node_modules
) -
$GOPATH
下的vendor
目錄 -
$GOROOT
下的src
目錄 -
$GOPATH
下的src
目錄
配置開關
有一點需要注意,在 go 1.5 版本下,go vendor
並不是預設開啟的,需要手動配置環境變數:
export GO15VENDOREXPERIMENT=1
在 go 1.6 版本中,go vendor
已經改為預設開啟。
Go Modules
雖然 1.5 版本推出了 go vendor
,但是沒有解決根本問題,只是依賴的查詢上支援到了 vendor
目錄,vendor
目錄還是需要一些第三方的庫(govendor
、godep
、glide
)進行管理,而且對於 GOPATH
環境變數依然有所依賴。
官方為了解決這些問題,終於在 1.11 版本中,實驗性的內建了其模組管理的能力(1.12 版本正式開啟):go mod
。
使用 go mod
的時候,我們無需 GOPATH
,所以我們需要把之前配置的 GOPATH
清理掉,調整下目錄結構,將 go-story/hello/main.go
直接移動到 go-story/main.go
,然後將 src
、pkg
目錄刪除。
# 初始化 go modules
go mod init [pkg-name]

此時,會在目錄下生成一個 go.mod
檔案。

檢視其內容,發現裡面會宣告 go 的版本號,以及當前模組的名稱。

然後我們安裝依賴(不管是何種依賴管理的方式,安裝方法依舊不變):
go get github.com/TreyBastian/colourize

go.mod
中,會寫入新增的依賴,以及版本號,同時,該模組會被安裝到 GOPATH 中。由於我們之前將 GOPATH 移除,這裡會安裝到 GOPATH 的預設值中(~/go/
)。

總結
之前開發 node 的過程中,也踩過很多 npm 的坑,而且社群對 npm 也有很多怨言,也出現了很多第三方的模組:yarn
、pnpm
等等。
想不到 go 的模組管理,也是一部血淚史,現在下載一些 go 的老專案還會發現一些 go vendor
管理方式的專案。另外,go mod
出現後,go 官方也在計劃移除 GOPATH
。
本文分享自微信公眾號 - 更了不起的前端(shenfq777)。
如有侵權,請聯絡 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。
- 詳解 Webpack devtools
- 什麼是 LFU 演算法?
- 什麼是 LFU 演算法?
- 關於 Promise 的執行順序
- 關於 Promise 的執行順序
- 新一代的編譯工具 SWC
- 介紹一個請求庫 — Undici
- 你給開源專案提過 PR 嗎?
- Go 語言的模組化
- MobX 上手指南
- 介紹兩種 CSS 方法論
- 普通打工人的2020丨掘金年度徵文
- Node.js 服務效能翻倍的祕密(二)
- Node.js 服務效能翻倍的祕密(一)
- 我是如何閱讀原始碼的
- Vue3 Teleport 元件的實踐及原理
- Vue3 模板編譯優化
- 小程式依賴分析實踐
- React 架構的演變 - Hooks 的實現
- Vue 3 的組合 API 如何請求資料?