Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
規(guī)范旨在為日常Go項(xiàng)目開發(fā)提供一個(gè)代碼的規(guī)范指導(dǎo),方便團(tuán)隊(duì)形成一個(gè)統(tǒng)一的代碼風(fēng)格,提高代碼的可讀性,規(guī)范性和統(tǒng)一性。本規(guī)范將從命名規(guī)范,注釋規(guī)范,代碼風(fēng)格和 Go 語言提供的常用的工具這幾個(gè)方面做一個(gè)說明。該規(guī)范參考了 go 語言官方代碼的風(fēng)格制定。
命名是代碼規(guī)范中很重要的一部分,統(tǒng)一的命名規(guī)則有利于提高的代碼的可讀性,好的命名僅僅通過命名就可以獲取到足夠多的信息。
Go在命名時(shí)以字母a到Z或a到Z或下劃線開頭,后面跟著零或更多的字母、下劃線和數(shù)字(0到9)。Go不允許在命名時(shí)中使用@、$和%等標(biāo)點(diǎn)符號(hào)。Go是一種區(qū)分大小寫的編程語言。因此,Manpower和manpower是兩個(gè)不同的命名。
當(dāng)命名(包括常量、變量、類型、函數(shù)名、結(jié)構(gòu)字段等等)以一個(gè)大寫字母開頭,如:Group1,那么使用這種形式的標(biāo)識(shí)符的對(duì)象就可以被外部包的代碼所使用(客戶端程序需要先導(dǎo)入這個(gè)包),這被稱為導(dǎo)出(像面向?qū)ο笳Z言中的 public)。 命名如果以小寫字母開頭,則對(duì)包外是不可見的,但是他們?cè)谡麄€(gè)包的內(nèi)部是可見并且可用的(像面向?qū)ο笳Z言中的 private )。
保持package的名字和目錄保持一致,盡量采取有意義的包名,簡短,有意義,盡量和標(biāo)準(zhǔn)庫不要沖突。包名應(yīng)該為小寫單詞,不要使用下劃線或者混合大小寫。
package demo package main
盡量采取有意義的文件名,簡短,有意義,應(yīng)該為小寫單詞,使用下劃線分隔各個(gè)單詞。
my_test.go
采用駝峰命名法,首字母根據(jù)訪問控制大寫或者小寫。
struct 申明和初始化格式采用多行,例如下面:
// 多行申明 type User struct{ Username string Email string } ? // 多行初始化 u :=User{ Username: "avatar", Email: "avatar@gmail.com", }
命名規(guī)則基本和上面的結(jié)構(gòu)體類型。
單個(gè)函數(shù)的結(jié)構(gòu)名以 “er” 作為后綴,例如 Reader , Writer 。
type Reader interface { Read(p []byte) (n int, err error) }
var isExist bool var hasConflict bool var canManage bool var allowGitHook bool
常量均需使用全部大寫字母組成,并使用下劃線分詞。
const APP_VER="1.0"
如果是枚舉類型的常量,需要先創(chuàng)建相應(yīng)類型:
type Scheme string ? const ( HTTP Scheme="http" HTTPS Scheme="https" )
下面的列表顯示了Go中的保留字。這些保留字不能用作常量或變量或任何其他標(biāo)識(shí)符名稱。
Go提供C風(fēng)格的/ /塊注釋和C ++風(fēng)格的//行注釋。行注釋是常態(tài);塊注釋主要顯示為包注釋,但在表達(dá)式中很有用或禁用大量代碼。
go 語言自帶的 godoc 工具可以根據(jù)注釋生成文檔,生成可以自動(dòng)生成對(duì)應(yīng)的網(wǎng)站( http://golang.org 就是使用 godoc 工具直接生成的),注釋的質(zhì)量決定了生成的文檔的質(zhì)量。每個(gè)包都應(yīng)該有一個(gè)包注釋,在package子句之前有一個(gè)塊注釋。對(duì)于多文件包,包注釋只需要存在于一個(gè)文件中,任何一個(gè)都可以。包評(píng)論應(yīng)該介紹包,并提供與整個(gè)包相關(guān)的信息。它將首先出現(xiàn)在godoc頁面上,并應(yīng)設(shè)置下面的詳細(xì)文檔。
詳細(xì)的如何寫注釋可以 參考:http://golang.org/doc/effective_go.html#commentary
每個(gè)包都應(yīng)該有一個(gè)包注釋,一個(gè)位于package子句之前的塊注釋或行注釋。包如果有多個(gè)go文件,只需要出現(xiàn)在一個(gè)go文件中(一般是和包同名的文件)即可。 包注釋應(yīng)該包含下面基本信息(請(qǐng)嚴(yán)格按照這個(gè)順序,簡介,創(chuàng)建人,創(chuàng)建時(shí)間):
例如 util 包的注釋示例如下:
// util 包, 該包包含了項(xiàng)目共用的一些常量,封裝了項(xiàng)目中一些共用函數(shù)。 // 創(chuàng)建人: xlli5 // 創(chuàng)建時(shí)間: 20190522
每個(gè)自定義的結(jié)構(gòu)體或者接口都應(yīng)該有注釋說明,該注釋對(duì)結(jié)構(gòu)進(jìn)行簡要介紹,放在結(jié)構(gòu)體定義的前一行,格式為: 結(jié)構(gòu)體名, 結(jié)構(gòu)體說明。同時(shí)結(jié)構(gòu)體內(nèi)的每個(gè)成員變量都要有說明,該說明放在成員變量的后面(注意對(duì)齊),實(shí)例如下:
// User , 用戶對(duì)象,定義了用戶的基礎(chǔ)信息 type User struct{ Username string // 用戶名 Email string // 郵箱 }
每個(gè)函數(shù),或者方法(結(jié)構(gòu)體或者接口下的函數(shù)稱為方法)都應(yīng)該有注釋說明,函數(shù)的注釋應(yīng)該包括三個(gè)方面(嚴(yán)格按照此順序撰寫):
示例如下:
// NewtAttrModel , 屬性數(shù)據(jù)層操作類的工廠方法 // 參數(shù): // ctx : 上下文信息 // 返回值: // 屬性操作類指針 func NewAttrModel(ctx *common.Context) *AttrModel { }
對(duì)于一些關(guān)鍵位置的代碼邏輯,或者局部較為復(fù)雜的邏輯,需要有相應(yīng)的邏輯說明,方便其他開發(fā)者閱讀該段代碼,實(shí)例如下:
// 從 Redis 中批量讀取屬性,對(duì)于沒有讀取到的 id , 記錄到一個(gè)數(shù)組里面,準(zhǔn)備從 DB 中讀取 xxxxx xxxxxxx xxxxxxx
統(tǒng)一使用中文注釋,對(duì)于中英文字符之間嚴(yán)格使用空格分隔, 這個(gè)不僅僅是中文和英文之間,英文和中文標(biāo)點(diǎn)之間也都要使用空格分隔,例如:
// 從 Redis 中批量讀取屬性,對(duì)于沒有讀取到的 id , 記錄到一個(gè)數(shù)組里面,準(zhǔn)備從 DB 中讀取
上面 Redis 、 id 、 DB 和其他中文字符之間都是用了空格分隔。
建議全部使用單行注釋 和代碼的規(guī)范一樣,單行注釋不要過長,禁止超過 120 字符
我們使用Goland開發(fā)工具,可以直接使用快捷鍵:ctrl+alt+L,即可。
Go語言中是不需要類似于Java需要冒號(hào)結(jié)尾,默認(rèn)一行就是一條數(shù)據(jù)
如果你打算將多個(gè)語句寫在同一行,它們則必須使用。
括號(hào)和空格方面,也可以直接使用 gofmt 工具格式化(go 會(huì)強(qiáng)制左大括號(hào)不換行,換行會(huì)報(bào)語法錯(cuò)誤),所有的運(yùn)算符和操作數(shù)之間要留空格。
// 正確的方式 if a > 0 { ? } ? // 錯(cuò)誤的方式 if a>0 // a ,0 和 > 之間應(yīng)該空格 { // 左大括號(hào)不可以換行,會(huì)報(bào)語法錯(cuò)誤 ? }
import在多行的情況下,goimports會(huì)自動(dòng)幫你格式化,但是我們這里還是規(guī)范一下import的一些規(guī)范,如果你在一個(gè)文件里面引入了一個(gè)package,還是建議采用如下格式:
import ( "fmt" )
如果你的包引入了三種類型的包,標(biāo)準(zhǔn)庫包,程序內(nèi)部包,第三方包,建議采用如下方式進(jìn)行組織你的包:
import ( "encoding/json" "strings" ? "myproject/models" "myproject/controller" "myproject/utils" ? "github.com/astaxie/beego" "github.com/go-sql-driver/mysql" )
有順序的引入包,不同的類型采用空格分離,第一種實(shí)標(biāo)準(zhǔn)庫,第二是項(xiàng)目包,第三是第三方包。
在項(xiàng)目中不要使用相對(duì)路徑引入包:
// 這是不好的導(dǎo)入 import "../net" ? // 這是正確的做法 import "github.com/repo/proj/src/net"
但是如果是引入本項(xiàng)目中的其他包,最好使用相對(duì)路徑。
// 錯(cuò)誤寫法 if err !=nil { // error handling } else { // normal code } ? // 正確寫法 if err !=nil { // error handling return // or continue, etc. } // normal code
單元測試文件名命名規(guī)范為 example_test.go 測試用例的函數(shù)名稱必須以 Test 開頭,盡量避免使用 main 方法測試。例如:TestExample 每個(gè)重要的函數(shù)都要首先編寫測試用例,測試用例和正規(guī)代碼一起提交方便進(jìn)行回歸測試。
編寫代碼時(shí)提供三套配置文件,分別是開發(fā)環(huán)境 [dev] , 測試環(huán)境 [test] , 現(xiàn)網(wǎng)環(huán)境 [prod] 。 目錄如下:
./ ├── dev │ ├── seelog.xml │ └── service.yml ├── prod │ ├── seelog.xml │ └── service.yml └── test ├── seelog.xml └── service.yml
go 語言本身在代碼規(guī)范性這方面也做了很多努力,很多限制都是強(qiáng)制語法要求,例如左大括號(hào)不換行,引用的包或者定義的變量不使用會(huì)報(bào)錯(cuò),此外 go 還是提供了很多好用的工具幫助我們進(jìn)行代碼的規(guī)范,
gofmt 大部分的格式問題可以通過gofmt解決, gofmt 自動(dòng)格式化代碼,保證所有的 go 代碼與官方推薦的格式保持一致,于是所有格式有關(guān)問題,都以 gofmt 的結(jié)果為準(zhǔn)。
goimport 我們強(qiáng)烈建議使用 goimport ,該工具在 gofmt 的基礎(chǔ)上增加了自動(dòng)刪除和引入包。
go get golang.org/x/tools/cmd/goimports
go vet vet工具可以幫我們靜態(tài)分析我們的源碼存在的各種問題,例如多余的代碼,提前return的邏輯,struct的tag是否符合標(biāo)準(zhǔn)等。
go get golang.org/x/tools/cmd/vet
使用如下:
go vet .
GoModule
Golang官方在1.11版本初步引入的GoModule模塊。1.12版本正式開始支持。GoModule是官方提供的包管理解決方案。通過GoModule,開發(fā)者可以把工程放在GOPATH之外的位置。相比于之前的包管理方案: dep,vendor。GoModule的管理方案更加靈活。 可以運(yùn)行g(shù)o mod help來看看GoModule中有哪些命令。
亮點(diǎn):
使用replace替換無法直接獲取的package 依賴包沖突問題 自動(dòng)查找包依賴
兩個(gè)等級(jí): [S] 建議, [M] 必須。以下是細(xì)節(jié)。
總而言之,文件名和目錄名,包名都必須小寫。數(shù)據(jù)類型變量和參數(shù)等定義最好使用駝峰大小寫法,不要使用下劃線或者中劃線
用了近十年的 C# 轉(zhuǎn)到 Go 是一個(gè)有趣的旅程。有時(shí),我陶醉于 Go 的簡潔[1];也有些時(shí)候,當(dāng)熟悉的 OOP (面向?qū)ο缶幊蹋?strong>模式[2]無法在 Go 代碼中使用的時(shí)候會(huì)感到沮喪。幸運(yùn)的是,我已經(jīng)摸索出了一些寫 HTTP 服務(wù)的模式,在我的團(tuán)隊(duì)中應(yīng)用地很好。
當(dāng)在公司項(xiàng)目上工作時(shí),我傾向把可發(fā)現(xiàn)性放在最高的優(yōu)先級(jí)上。這些應(yīng)用會(huì)在接下來的 20 年運(yùn)行在生產(chǎn)環(huán)境中,必須有眾多的開發(fā)人員和網(wǎng)站可靠性工程師(可能是指運(yùn)維)來進(jìn)行熱補(bǔ)丁,維護(hù)和調(diào)整工作。因此,我不指望這些模式能適合所有人。
Mat Ryer 的文章[3]是我使用 Go 試驗(yàn) HTTP 服務(wù)的起點(diǎn)之一,也是這篇文章的靈感來源。
一個(gè) Broker 結(jié)構(gòu)是將不同的 service 包綁定到 HTTP 邏輯的膠合結(jié)構(gòu)。沒有包作用域結(jié)級(jí)別的變量被使用。依賴的接口得益于了 Go 的組合[4]的特點(diǎn)被嵌入了進(jìn)來。
type Broker struct {
auth.Client // 從外部倉庫導(dǎo)入的身份驗(yàn)證依賴(接口)
service.Service // 倉庫的業(yè)務(wù)邏輯包(接口)
cfg Config // 該 API 服務(wù)的配置
router *mux.Router // 該 API 服務(wù)的路由集
}
broker 可以使用阻塞[5]函數(shù) New() 來初始化,該函數(shù)校驗(yàn)配置,并且運(yùn)行所有需要的前置檢查。
func New(cfg Config, port int) (*Broker, error) {
r := &Broker{
cfg: cfg,
}
...
r.auth.Client, err = auth.New(cfg.AuthConfig)
if err != nil {
return nil, fmt.Errorf("Unable to create new API broker: %w", err)
}
...
return r, nil
}
初始化后的 Broker 滿足了暴露在外的 Server 接口,這些接口定義了所有的,被 route 和 中間件(middleware)使用的功能。service 包接口被嵌入,這些接口與 Broker 上嵌入的接口相匹配。
type Server interface {
PingDependencies(bool) error
ValidateJWT(string) error
service.Service
}
web 服務(wù)通過調(diào)用 Start() 函數(shù)來啟動(dòng)。路由綁定通過一個(gè)閉包函數(shù)[6]進(jìn)行綁定,這種方式保證循環(huán)依賴不會(huì)破壞導(dǎo)入周期規(guī)則。
func (bkr *Broker) Start(binder func(s Server, r *mux.Router)) {
...
bkr.router = mux.NewRouter().StrictSlash(true)
binder(bkr, bkr.router)
...
if err := http.Serve(l, bkr.router); errors.Is(err, http.ErrServerClosed) {
log.Warn().Err(err).Msg("Web server has shut down")
} else {
log.Fatal().Err(err).Msg("Web server has shut down unexpectedly")
}
}
那些對(duì)故障排除(比如,Kubernetes 探針[7])或者災(zāi)難恢復(fù)方案方面有用的函數(shù),掛在 Broker 上。如果被 routes/middleware 使用的話,這些僅僅被添加到 webserver.Server 接口上。
func (bkr *Broker) SetupDatabase() { ... }
func (bkr *Broker) PingDependencies(failFast bool)) { ... }
整個(gè)應(yīng)用的入口是一個(gè) main 包。默認(rèn)會(huì)啟動(dòng) Web 服務(wù)。我們可以通過傳入一些命令行參數(shù)來調(diào)用之前提到的故障排查功能,方便使用傳入 New() 函數(shù)的,經(jīng)過驗(yàn)證的配置來測試代理權(quán)限以及其他網(wǎng)絡(luò)問題。我們所要做的只是登入運(yùn)行著的 pod 然后像使用其他命令行工具一樣使用它們。
func main() {
subCommand := flag.String("start", "", "start the webserver")
...
srv := webserver.New(cfg, 80)
switch strings.ToLower(subCommand) {
case "ping":
srv.PingDependencies(false)
case "start":
srv.Start(BindRoutes)
default:
fmt.Printf("Unrecognized command %q, exiting.", subCommand)
os.Exit(1)
}
}
HTTP 管道設(shè)置在 BindRoutes() 函數(shù)中完成,該函數(shù)通過 ser.Start() 注入到服務(wù)(server)中。
func BindRoutes(srv webserver.Server, r *mux.Router) {
r.Use(middleware.Metrics(), middleware.Authentication(srv))
r.HandleFunc("/ping", routes.Ping()).Methods(http.MethodGet)
...
r.HandleFunc("/makes/{makeID}/models/{modelID}", model.get(srv)).Methods(http.MethodGet)
}
中間件(Middleware)返回一個(gè)帶有 handler 的函數(shù),handler 用來構(gòu)建需要的 http.HandlerFunc。這使得 webserver.Server 接口被注入,同時(shí)所有的安靜檢查只在啟動(dòng)時(shí)執(zhí)行,而不是在所有路由調(diào)用的時(shí)候。
func Authentication(srv webserver.Server) func(h http.Handler) http.Handler {
if srv == nil || !srv.Client.IsValid() {
log.Fatal().Msg("a nil dependency was passed to authentication middleware")
}
// additional setup logic
...
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := strings.TrimSpace(r.Header.Get("Authorization"))
if err := srv.ValidateJWT(token); err != nil {
...
w.WriteHeader(401)
w.Write([]byte("Access Denied"))
return
}
next.ServeHTTP(w, r)
}
}
}
路由有著與中間件有著類似的套路——簡單的設(shè)置,但是有著同樣的收益。
func GetLatest(srv webserver.Server) http.HandlerFunc {
if srv == nil {
log.Fatal().Msg("a nil dependency was passed to the `/makes/{makeID}/models/{modelID}` route")
}
// additional setup logic
...
return func(w http.ResponseWriter, r *http.Request) {
...
makeDTO, err := srv.Get
}
}
代碼的目錄結(jié)構(gòu)對(duì)可發(fā)現(xiàn)性進(jìn)行了高度優(yōu)化。
├── app/
| └── service-api/**
├── cmd/
| └── service-tool-x/
├── internal/
| └── service/
| └── mock/
├── pkg/
| ├── client/
| └── dtos/
├── (.editorconfig, .gitattributes, .gitignore)
└── go.mod
最重要的:每個(gè)包只負(fù)責(zé)意見事情,一件事情!
└── service-api/
├── cfg/
├── middleware/
├── routes/
| ├── makes/
| | └── models/**
| ├── create.go
| ├── create_test.go
| ├── get.go
| └── get_test.go
├── webserver/
├── main.go
└── routebinds.go
如果你最終采用了這種模式,或者有其他的想法我們可以討論,我樂意聽到這些想法!
via: https://www.dudley.codes/posts/2020.05.19-golang-structure-web-servers/
作者:James Dudley[12]譯者:dust347[13]校對(duì):unknwon[14]
本文由 GCTT[15] 原創(chuàng)編譯,Go 中文網(wǎng)[16] 榮譽(yù)推出
[1]
簡潔: https://www.youtube.com/watch?v=rFejpH_tAHM
[2]
模式: https://en.wikipedia.org/wiki/Software_design_pattern
[3]
Mat Ryer 的文章: https://pace.dev/blog/2018/05/09/how-I-write-http-services-after-eight-years.html
[4]
Go 的組合: https://www.ardanlabs.com/blog/2015/09/composition-with-go.html
[5]
阻塞: https://stackoverflow.com/questions/2407589/what-does-the-term-blocking-mean-in-programming
[6]
閉包函數(shù): https://gobyexample.com/closures
[7]
Kubernetes 探針: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)0
[8]
特殊目錄: https://dave.cheney.net/2019/10/06/use-internal-packages-to-reduce-your-public-api-surface
[9]
dogfood it: https://en.wikipedia.org/wiki/Eating_your_own_dog_food
[10]
所有的倉庫必須使用 .editorconfig,.gitattributes,.gitignore: https://www.dudley.codes/posts/2020.02.16-git-lost-in-translation/
[11]
有限制的且官僚的公司環(huán)境: https://www.dudley.codes/posts/2020.04.02-golang-behind-corporate-firewall/
[12]
James Dudley: https://www.dudley.codes/
[13]
dust347: https://github.com/dust347
[14]
unknwon: https://github.com/unknwon
[15]
GCTT: https://github.com/studygolang/GCTT
[16]
Go 中文網(wǎng): https://studygolang.com/
想構(gòu)建一個(gè)本地 Go 桌面應(yīng)用程序,有幾種方法可以做到:
我已經(jīng)寫過有關(guān)構(gòu)建一個(gè)簡單的 electron 應(yīng)用程序的文章[10],因此本文將探討如何使用 Lorca 和 Webview 構(gòu)建應(yīng)用程序,然后比較這三種的不同。
Go 中一個(gè)簡單的 Lorca[11] 應(yīng)用:
func main() {
// Create UI with data URI
ui, _ := lorca.New("data:text/html,"+url.PathEscape(`
<html>
<head><title>Hello</title></head>
<body><h1>Hello, world!</h1></body>
</html>
`), "", 600, 200)
defer ui.Close()
// Create a GoLang function callable from JS
ui.Bind("hello", func() string { return "World!" })
// Call above `hello` function then log to the JS console
ui.Eval("hello().then( (x) => { console.log(x) })")
// Wait until UI window is closed
<-ui.Done()
}
因?yàn)閺?fù)雜性被隱藏了,所以看起來非常簡單!上面的代碼打開一個(gè) Chome 窗口,通過 websocket 連接到其 dev-tools[12] 端點(diǎn),發(fā)送要加載的 HTML,并提供 Go 和 JS 之間的通信:
更酷的是,您可以在 Chrome 中調(diào)用 JS 函數(shù)并在 Go 中獲取輸出:
n := ui.Eval(`Math.random()`).Float()
fmt.Println(n)
使用這個(gè)庫是如此容易,如此直觀,如此實(shí)用,以至于我剛使用它時(shí)感到困惑。我以為一定有陷阱,不會(huì)這么簡單。但是沒有,它就是這么簡單。
另外一個(gè)好處是您可以使用 Chrome 開發(fā)工具來幫助調(diào)試任何問題或調(diào)整布局。另外,鑒于我自 2014 年以來[13]一直在寫 Promise,我喜歡使用 JS Promise 在 Go 和 JS 之間實(shí)現(xiàn)異步調(diào)用。
Lorca 的最大缺點(diǎn)是,由于它使用 Chrome,因此某些應(yīng)用程序詳細(xì)信息(如系統(tǒng)菜單,圖標(biāo),標(biāo)題)無法自定義。然后,需要在應(yīng)用程序優(yōu)化和簡單應(yīng)用程序之間進(jìn)行權(quán)衡。根據(jù)您構(gòu)建內(nèi)容的不同,有好有弊,例如,如果您正在構(gòu)建內(nèi)部工具,那會(huì)很好,但是對(duì)于企業(yè)應(yīng)用程序,這可能看起來并不好。
Webview[14] 是一個(gè)庫,可幫助直接在本地組件之上構(gòu)建 Web 應(yīng)用程序。執(zhí)行此操作的代碼如下:
func main() {
w := webview.New(true)
defer w.Destroy()
w.SetSize(600, 200, webview.HintNone)
// Create a GoLang function callable from JS
w.Bind("hello", func() string { return "World!" })
// Create UI with data URI
w.Navigate(`data:text/html,
<!doctype html>
<html>
<head><title>Hello</title></head>
<body><h1>Hello, world!</h1></body>
<script> hello().then((x) => { console.log(x) }) </script>
</html>`)
w.Run()
}
這與 Lorca 非常相似,我認(rèn)為 Lorca 也是基于 Webview 的。盡管與 Lorca 相似,但輸出還是有些不同:
從上圖可以看到 Webview 應(yīng)用程序窗口沒有陰影,沒有邊框,并且在屏幕的左下角進(jìn)行了初始化。可以通過將Window返回一個(gè) unsafe.Pointer到 OS 依賴的窗口對(duì)象的方法(在 macOS 中是 NSWindow)進(jìn)行定制。這是開始難的地方。
要使用該 Window 對(duì)象,我們必須將 Go 的綁定寫入本地組件。舉例來說,如果我們希望我們的窗口居中啟動(dòng),我們會(huì)調(diào)用 NSWindow 的 Center 方法。因此,我們需要在三個(gè)文件中寫綁定(改編自 gogoa[15]):
ns_window.go
package main
// #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework Cocoa
//#include "ns_window.h"
import "C"
import "unsafe"
type NSWindow struct {
ptr unsafe.Pointer
}
func (self *NSWindow) Center() {
C.Center(self.ptr)
}
ns_window.h
#include <Cocoa/Cocoa.h>
void Center(void *);
ns_window.m
#include "ns_window.h"
void Center(void *self) {
NSWindow *window = self;
[window center];
}
然后在main()函數(shù)中,我們可以將窗口居中:
window := NSWindow {w.Window()}
window.Center()
與 Lorca 不同,Webview 可以針對(duì)我們的應(yīng)用程序進(jìn)行完全自定義。問題在于它需要一些工作。
Webview 的一些其他部分使得用它變得有些困難:
我不確定有多少是Webview,有多少是 NSWindow。我需要進(jìn)行更多的調(diào)查和學(xué)習(xí),才能更清楚地說明這些發(fā)生的原因。
我之前的文章[16]是關(guān)于構(gòu)建一個(gè)簡單的 Electron 應(yīng)用程序的,該應(yīng)用程序如下所示:
Electron 用于許多大型產(chǎn)品,例如 VSCode。這可能是因?yàn)閷⑺袃?nèi)容捆綁到一個(gè)應(yīng)用程序中使可移植性變得更加簡單,并且可以廣泛地定制應(yīng)用程序。將應(yīng)用程序與瀏覽器和 Node.js 捆綁在一起的不利之處在于,它導(dǎo)致程序 非常龐大。
讓 Go 與 Electron 一起工作也有些困難。但有一些框架可以簡化[17]此過程,例如 go-astilectron[18],不過這些框架很復(fù)雜,并且大多數(shù)功能不完整。另一種方法可能是使用我之前寫過的[19] Go 編譯為 WASM ,但這也不是簡單的解決方案。
Electron 的優(yōu)勢在于它是便攜式的,可定制的,并且經(jīng)過了應(yīng)用程序分發(fā)的嚴(yán)格測試。只是和 Go 結(jié)合有點(diǎn)復(fù)雜。
我認(rèn)為要進(jìn)行的主要比較是可定制性與簡單性。到目前為止,Lorca 是最簡單的,其可定制性非常有限,Webview 可以完全自定義,但有些困難,而 Electron 則可以完全自定義,但很難與 Go 一起使用。
同樣,框架之間的捆綁包大小也有很大差異。Lorca 的二進(jìn)制文件大小為 8.7 MB,Webview 的大小為 3.7Mb,Electron 的大小為 157Mb。
調(diào)試工具也有所不同:Lorca 和 Electron 使用 Chrome 開發(fā)工具,而 Webview 使用 Safari 開發(fā)工具。
Lorca 和 Webview 都可以與 Go 一起很好地使用,最終二進(jìn)制較小,并且具有類似的 API。主要區(qū)別在于基礎(chǔ)渲染器(本機(jī))和調(diào)試工具。
我認(rèn)為 Electron 與 Go 一起使用可能太復(fù)雜了,但沒有太多困難。
一個(gè)潛在的工作流程是在開發(fā)和 Webview 分發(fā)期間使用 Lorca。Lorca 提供了用于調(diào)試和開發(fā)的熟悉工具,其中 Webview 提供了可分發(fā)的可定制性。Lorca 也是很好的備份,可以交叉編譯到 Webview 不支持的其他操作系統(tǒng)。
注意:還有更多類似的選項(xiàng),wails[20] 或 gotk[21] 可以提供其他方式來構(gòu)建/分發(fā)應(yīng)用程序。
作者:Graham Jenson
原文鏈接:https://maori.geek.nz/golang-desktop-app-webview-vs-lorca-vs-electron-a5e6b2869391
譯者:polaris
[1]
Electron: https://www.electronjs.org/
[2]
Node.js: https://nodejs.org/
[3]
Chromium: https://www.chromium.org/
[4]
go-app: https://github.com/maxence-charriere/go-app
[5]
go-astilectron: https://github.com/asticode/go-astilectron
[6]
Lorca: https://github.com/zserge/lorca
[7]
dev-tools communication protocol: https://chromedevtools.github.io/devtools-protocol/
[8]
Webview: https://github.com/webview/webview
[9]
webview: https://developer.apple.com/documentation/webkit/webview
[10]
構(gòu)建一個(gè)簡單的 electron 應(yīng)用程序的文章: https://maori.geek.nz/building-an-electron-app-with-bazel-d124ed550957
[11]
Lorca: https://github.com/zserge/lorca
[12]
dev-tools: https://chromedevtools.github.io/devtools-protocol/
[13]
自 2014 年以來: https://maori.geek.nz/jquery-promises-and-deferreds-i-promise-this-will-be-short-d10275f82717
[14]
Webview: https://github.com/webview/webview
[15]
gogoa: https://github.com/alediaferia/gogoa
[16]
之前的文章: https://maori.geek.nz/building-an-electron-app-with-bazel-d124ed550957
[17]
簡化: https://github.com/asticode/go-astilectron
[18]
go-astilectron: https://github.com/asticode/go-astilectron
[19]
之前寫過的: https://maori.geek.nz/a-web-app-using-bazel-golang-wasm-and-proto-c020914f4341
[20]
wails: https://github.com/wailsapp/wails
[21]
gotk: https://github.com/gotk3/gotk3
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。