標(biāo)準(zhǔn)模板包的概述
在本文中,我將解釋 Go 語(yǔ)言(Golang)的標(biāo)準(zhǔn)模板包的基礎(chǔ)知識(shí)。這些基礎(chǔ)知識(shí)包括在 Golang 模板中使用變量、條件語(yǔ)句、遍歷變量以及將函數(shù)應(yīng)用于變量。
Golang 提供了 text/template 和 html/template 包,以便直接處理模板。
第一個(gè)包是最通用的一個(gè)——你可以用它來(lái)創(chuàng)建所有種類的文本字符串的模板。第二個(gè)包更具針對(duì)性地用于 HTML——它在處理 HTML 網(wǎng)頁(yè)環(huán)境中的不安全變量時(shí)非常方便。
這些包含有各種可以加載、解析和評(píng)估模板文本或(HTML 或文本)文件的函數(shù)。
例如,你可以使用以下函數(shù):
- 使用
Parse解析程序內(nèi)部存在的文本字符串。 - 使用
ParseFiles加載和解析模板文件。 - 使用
Execute通過(guò)特定的數(shù)據(jù)字段將模板渲染到某些輸出。
接下來(lái),我將討論在 Golang 中創(chuàng)建強(qiáng)大模板的基本構(gòu)建模塊。
外部(程序)變量
你可以從實(shí)際的 Go 程序發(fā)送變量到你的模板。然后你可以在模板中使用這些變量。
首先,當(dāng)你希望在模板中渲染特定的操作時(shí),你可以通過(guò)在文本字符串中添加雙括號(hào) {{}} 來(lái)實(shí)現(xiàn)這一點(diǎn)。
我們可以利用這個(gè)特性來(lái)顯示你從程序中提供的變量。所以,如果你在雙括號(hào)中添加一個(gè)點(diǎn),所有的數(shù)據(jù)字段都將在那里被渲染。
例如,{{.}} 將把你所有的數(shù)據(jù)字段渲染為一個(gè)格式化的字符串。
此外,可以通過(guò)指定該字段的名稱來(lái)訪問(wèn)你的數(shù)據(jù)的特定字段。
例如,{{ .Title }} 只會(huì)將 Title 字段渲染為一個(gè)格式化的字符串。
你可以在下面的代碼示例中看到這些操作的應(yīng)用。
package main
import (
"os"
"text/template"
)
type Book struct {
Title string
Publisher string
Year int
}
func main() {
t1 := template.New("Template")
t1, _ = t1.Parse("External variable has the value [{{.}}]\n")
t1.Execute(os.Stdout, "Amazing")
b := Book{"The CSound Book", "MIT Press", 2002}
t1.Execute(os.Stdout, b)
t2 := template.New("Template")
t2, _ = t2.Parse("External variable Book has the values [Title: {{.Title}}, Publisher: {{.Publisher}}, Year: {{.Year}}]\n")
t2.Execute(os.Stdout, b)
}
// Output
// External variable has the value [Amazing]
// External variable has the value [{The CSound Book MIT Press 2002}]
// External variable Book has the values [Title: The CSound Book, Publisher: MIT Press, Year: 2002]
在上述代碼中,我們首先使用 template.New() 創(chuàng)建一個(gè)新的空模板。然后我們使用 Parse() 將一個(gè)字符串解析到這個(gè)模板中。在這個(gè)字符串中,我們?cè)陔p括號(hào)間加入了一個(gè)動(dòng)作。
因?yàn)槲覀冊(cè)诶ㄌ?hào)之間放了一個(gè)點(diǎn),這向模板渲染器發(fā)出了一個(gè)信號(hào),即需要在這里渲染提供的變量。
使用 Execute() 來(lái)渲染帶有變量的模板。第一個(gè)參數(shù) (os.Stdout) 是渲染的模板要輸出的位置。第二個(gè)參數(shù) (Amazing) 是我們希望在模板中渲染的變量。在這種情況下,它只是一個(gè)字符串。
我們可以為模板提供的變量,比如說(shuō),一個(gè)結(jié)構(gòu)體,而不僅僅是一個(gè)字符串。在這里,我們創(chuàng)建了一個(gè)用于保存書(shū)籍?dāng)?shù)據(jù)的結(jié)構(gòu)體。然后,我們可以將其提供給我們之前創(chuàng)建的相同模板。
也可以渲染結(jié)構(gòu)體的單獨(dú)數(shù)據(jù)字段。為此,創(chuàng)建一個(gè)新的模板字符串。在雙括號(hào)之間,我們放一個(gè)點(diǎn),后面跟著我們希望渲染的結(jié)構(gòu)體數(shù)據(jù)字段的名稱,而不僅僅是一個(gè)點(diǎn)。請(qǐng)注意,這個(gè)數(shù)據(jù)字段是以大寫字母寫的,表示它是可導(dǎo)出的。
注意 Parse 函數(shù)實(shí)際上返回兩個(gè)變量。第一個(gè)變量是解析后的模板,第二個(gè)是錯(cuò)誤消息。在上述示例中,我們不會(huì)使用錯(cuò)誤消息,所以我們只放一個(gè)下劃線。
內(nèi)部(模板)變量
你可以定義內(nèi)部變量,即只在模板內(nèi)定義的變量。
下面的模式展示了如何在模板字符串中定義并使用一個(gè)變量。首先是定義,然后是使用。內(nèi)部變量應(yīng)該以美元符號(hào) ($) 開(kāi)頭。
{{$var:=`value`}} ... {{$var}} ...
下面是一個(gè)簡(jiǎn)單的例子:
package main
import (
"os"
"text/template"
)
func main() {
t, _ := template.New("Template").Parse("{{$var:=2150}}Internal variable has the value [{{$var}}]")
t.Execute(os.Stdout, nil)
}
// Output:
// Internal variable has the value [2150]
與使用點(diǎn)渲染外部變量不同,在這里我們提到了我們之前在雙括號(hào)之間定義的變量。
條件語(yǔ)句
以下是條件語(yǔ)句的一般模式:
{{if [..]}} if-part {{end}}
{{if [..]}} if-part {{else}} else-part {{end}}
{{if [..]}} if-part {{if else}} if-else-part {{end}}
在最后的模式中,你可以根據(jù)需要多次使用 {{if else}} 部分,也可以添加一個(gè) {{else}} 部分。以下是第二種模式的一個(gè)簡(jiǎn)單示例:
package main
import (
"os"
"text/template"
)
func main() {
t, err := template.New("Template").Parse("{{if eq . `filler`}}This is filler...{{else}}It's something else...{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(os.Stdout, "filler")
}
// Output:
// This is filler...
注意,在上面的例子中,我們也使用了從 Parse() 函數(shù)返回的錯(cuò)誤消息。如果沒(méi)有錯(cuò)誤,錯(cuò)誤消息等于 'nil'。如果有錯(cuò)誤,我們可以使用 panic() 函數(shù)來(lái)處理。
另外,你可以使用以下操作符,而不只是等于 eq(對(duì)應(yīng) ==):
- 非等于
ne(對(duì)應(yīng) !=) - 小于
lt(對(duì)應(yīng) <) - 小于等于
le(對(duì)應(yīng) <=) - 大于
gt(對(duì)應(yīng) >) - 大于等于
ge(對(duì)應(yīng) >=)
此外,你還可以直接寫入布爾變量,而不是比較語(yǔ)句。
package main
import (
"os"
"text/template"
)
func main() {
t, _ := template.New("Template").Parse("{{if .}}This is true.{{else}}This is false.{{end}}\n")
t.Execute(os.Stdout, false)
}
// Output:
// This is false
循環(huán)
在遍歷數(shù)組、切片或映射時(shí),你可以在模板中使用幾種模式。首先,我們來(lái)看看最簡(jiǎn)單的形式。
{{range .Var}}
{{.}}
{{end}}
在這里,我們遍歷數(shù)組、切片或映射(.Var)中的每個(gè)變量。在循環(huán)的每一步中,只有一個(gè)變量可供在循環(huán)中使用。
現(xiàn)在,{{.}}不再代表我們模板可用的所有變量,而只代表在循環(huán)中可用的那個(gè)變量。
package main
import (
"os"
"text/template"
)
func main() {
computerList := []string{"Arduino", "Raspberri Pi", "NVidia Jetson Nano"}
t, err := template.New("Template").Parse("My favorite computers are:\n{{range .}}{{.}}\n{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(os.Stdout, computerList)
}
// Output:
// My favorite computers are:
// Arduino
// Raspberri Pi
// NVidia Jetson Nano
當(dāng)你在一堆變量中進(jìn)行循環(huán)時(shí),可以使用以下更復(fù)雜的模式:
{{range $index, $element := . }}
{{$index}} ... {{$element}} ...
{{end}}
這個(gè)模式在你需要在循環(huán)中使用變量索引時(shí)非常方便。例如,如果你希望輸出一個(gè)有序的帶編號(hào)的列表,你可以使用這個(gè)模板模式。
注意,這里的變量 $index 和 $element 是在模板內(nèi)部定義的內(nèi)部變量。因此,它們也以 ''$' 符號(hào)開(kāi)頭。
package main
import (
"os"
"text/template"
)
func main() {
dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"}
t, err := template.New("Template").Parse("My favorite dishes are:\n{{range $index, $item:=.}}{{$index}}) {{$item}}\n{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(os.Stdout, dishesList)
}
// Output
// My favorite dishes are:
// 0) Enciladas con Pollo
// 1) Hot&Spicy Pizza
// 2) Spaghetti Bolognese
如你所見(jiàn),這種方法有一個(gè)問(wèn)題。我們從輸出中得到的有序列表從0開(kāi)始。原因是第一個(gè)索引總是0。
對(duì)于許多應(yīng)用來(lái)說(shuō),這并不是我們想要的。我們希望有序列表從1開(kāi)始。那么,我們?nèi)绾巫屆總€(gè)索引增加1呢?我們需要一個(gè)函數(shù)來(lái)增加給定的變量。
在下一部分,我們將看到如何在模板字符串中運(yùn)行函數(shù)。
在模板中使用函數(shù)
在模板中可以使用函數(shù)。
為此,你需要將你希望在模板中使用的函數(shù)映射到一個(gè)關(guān)鍵字。
在下面的例子中,我們將創(chuàng)建一個(gè)叫做 add() 的添加函數(shù)。我們將使用 template.FuncMap{} 在 FuncMap() 函數(shù)內(nèi)將其添加到函數(shù)映射中。
有了 add 函數(shù),我們將能夠向 $index 變量添加1,從而增加它。結(jié)果將是一個(gè)更易讀的有序列表。
package main
import (
"os"
"text/template"
)
func add(a, b int) int {
return a + b
}
func main() {
dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"}
t, err := template.New("Template").Funcs(template.FuncMap{"add": add}).Parse("My favorite dishes are:\n{{range $index, $item:=.}}{{add $index 1}}) {{$item}}\n{{end}}\n")
if err != nil {
panic(err)
}
t.Execute(os.Stdout, dishesList)
}
// Output:
// My favorite dishes are:
// 1) Enciladas con Pollo
// 2) Hot&Spicy Pizza
// 3) Spaghetti Bolognese
你可以在下面找到一個(gè)更復(fù)雜的例子。這里我們添加了第二個(gè)函數(shù)。想法是創(chuàng)建一個(gè)CSV風(fēng)格的輸出,但當(dāng)我們?cè)诤瘮?shù)映射中添加“分隔符”時(shí),我們可以選擇我們想要的分隔符。
你也可以看到,映射中使用的名稱并不是實(shí)際函數(shù)名稱。
package main
import (
"os"
"text/template"
)
func add(a, b int) int {
return a + b
}
func delimiter(s string) func() string {
return func() string {
return s
}
}
func main() {
dishesList := []string{"Enciladas con Pollo", "Hot&Spicy Pizza", "Spaghetti Bolognese"}
tmpl := "Index{{dl}}Dish\n{{range $index, $item:=.}}{{add $index 1}}{{dl}}{{$item}}\n{{end}}\n"
funcMap := template.FuncMap{"add": add, "dl": delimiter(",")}
t, _ := template.New("Template").Funcs(funcMap).Parse(tmpl)
t.Execute(os.Stdout, dishesList)
}
// Output:
// Index,Dish
// 1,Enciladas con Pollo
// 2,Hot&Spicy Pizza
// 3,Spaghetti Bolognese
有一個(gè)方便的庫(kù),叫做Sprig,它有模板函數(shù)映射。你可以在這里找到它。然而,這個(gè)庫(kù)似乎只能和html/template包一起使用。
列表清單
請(qǐng)記住,始終保持學(xué)習(xí)的態(tài)度,并享受編碼的樂(lè)趣!祝您編碼愉快!
如果你喜歡我的文章,點(diǎn)贊,關(guān)注,轉(zhuǎn)發(fā)!