Go 語言的模組化

語言: CN / TW / HK

前言

在很久很久以前,就 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-bloggo-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 目錄還是需要一些第三方的庫(govendorgodepglide)進行管理,而且對於 GOPATH 環境變數依然有所依賴。

官方為了解決這些問題,終於在 1.11 版本中,實驗性的內建了其模組管理的能力(1.12 版本正式開啟):go mod

使用 go mod 的時候,我們無需 GOPATH,所以我們需要把之前配置的 GOPATH 清理掉,調整下目錄結構,將 go-story/hello/main.go 直接移動到 go-story/main.go,然後將 srcpkg 目錄刪除。

# 初始化 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 也有很多怨言,也出現了很多第三方的模組:yarnpnpm 等等。

想不到 go 的模組管理,也是一部血淚史,現在下載一些 go 的老專案還會發現一些 go vendor 管理方式的專案。另外,go mod 出現後,go 官方也在計劃移除 GOPATH


本文分享自微信公眾號 - 更了不起的前端(shenfq777)。
如有侵權,請聯絡 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。