前言
在網頁程式中使用模板語言 (template language) 可以簡化生成網頁的任務。透過模板和資料的結合,我們可以動態地用程式產生頁面。網頁框架幾乎都會使用模板語言,但現在的模板語言通常會比通用型程式語言來得簡單得多;早期程式人濫用 PHP 造成「義大利麵」程式碼,我們不應該重覆這個反模式 (anti-pattern)。
Go 的模板語言的語法記錄在 text/template 套件,但實際要使用時,要使用 html/template 套件,而不是使用原本的 text/template 套件,因 http/template 套件會對程式碼插入 (code injection) 等情形進行相對應的防護,而 text/template 套件則沒有防護惡意程式碼的功能。
本文會介紹幾個常見的模板語言語法。
變數 (Variable)
由於本範例檔案較多,我們將範例放在這裡,讓讀者追蹤程式碼,這裡僅節錄部分內容。
我們先來看模板的部分:
<!DOCTYPE html>
<html>
<head>
<title>{{ .Title }}</title>
</head>
<body>
<h1>{{ .Title }}</h1>
</body>
</html>
我們的目標是將變數 {{ .Title }}
的地方代換掉。來看一下模板相關的程式碼:
func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var tmpl = template.Must(template.ParseFiles("views/index.html"))
tmpl.Execute(w, struct {
Title string
}{
"My Awesome Site",
})
}
在此處,我們用匿名結構 (anonymous struct) 帶入變數,這在 Go 語言中是一個常見的手法。以本例來說,{{ .Title }}
最後會代換成 "My Awesome Site"
。
程式執行結果如下:
if
敘述
我們將完整程式碼放在這裡,有需要的讀者可自行追蹤。
我們先來看模板的部分:
<!DOCTYPE html>
<html>
<head>
<title>Using if in html/template</title>
</head>
<body>
{{ if .Lang }}
<p>My favorite language is {{.Lang}}</p>
{{ end }}
</body>
</html>
要注意 Go 模板語言的 if
無法做出複雜的判斷式,僅能判斷變數的真偽。這是因為模板語言的目標只是要帶入資料,而非用來建立複雜的程式邏輯。
接著來看程式碼的部分:
func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var tmpl = template.Must(template.ParseFiles("views/index.html"))
tmpl.Execute(w, struct {
Lang string
}{
"Golang",
})
}
這部分同樣是帶入變數,沒什麼特別的地方。
程式執行結果如下:
range
敘述
range
是用來走訪容器 (collection) 的語法。同樣地,我們將完整的範例放在這裡,有需要的讀者可自行追蹤程式碼。
模板的部分如下:
<!DOCTYPE html>
<html>
<head>
<title>Using range in html/template</title>
</head>
<body>
<p>Major tier languages for web programming:</p>
<ul>
{{ range .Langs }}
<li>{{ . }}</li>
{{ end }}
</ul>
</body>
</html>
容器的元素會將 {{ . }}
的部分代換掉。用 range
做動態的網頁清單是常見的手法。
接著來看程式碼的部分:
func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var tmpl = template.Must(template.ParseFiles("views/index.html"))
tmpl.Execute(w, struct {
Langs []string
}{
[]string{"Python", "Ruby", "PHP", "Java", "Golang"},
})
}
在本範例中,我們的變數 Langs
所指向的資料型態為字串陣列,所以可在模板進行迭代。
程式執行的結果如下:
搭配映射 (Map) 的 range
敘述
在上一節中,我們用陣列搭配 range
,在本例中,我們改用映射來搭配。我們將完整的範例放在這裡,需要的讀者可自行追蹤程式碼。
模板的部分如下:
<!DOCTYPE html>
<html>
<head>
<title>Using range with map in html/template</title>
</head>
<body>
<p>Major web frameworks for web programming:</p>
<ul>
{{range $key, $value := .Frameworks}}
<li>{{ $key }} ({{ $value }})</li>
{{end}}
</ul>
</body>
</html>
在這個模板中,我們帶入新的變數 $key
和 $value
,在 range
區塊中就可以使用。
如果是陣列,帶入新變數的方式如下:
{{ range $index, $element := .Array }}
{{/* Use the variables here */}}
{{ end }}
接著來看程式碼的部分:
func index(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
var tmpl = template.Must(template.ParseFiles("views/index.html"))
tmpl.Execute(w, struct {
Frameworks map[string]string
}{
map[string]string{
"Django": "Python",
"Rails": "Ruby",
"Laravel": "PHP",
"Spring": "Java",
"Gin": "Golang",
},
})
}
在此處,變數 Frameworks
指向的資料型態為映射,該映射的鍵和值皆為字串。帶入的映射可在模板中迭代。
程式執行結果如下:
結語
在本文中,我們介紹了 Go 網頁程式的模板語言,應該足以開始撰寫靜態網頁。
現在的模板語言不會像 PHP 那麼複雜,因為程式人從 PHP 的歷史教訓可知模板語言應該要回歸其原本的角色,而非把所有的程式邏輯都塞在模板中。