Go 視圖模板篇(一):模板引擎的定義、解析與執(zhí)行
1、模板和模板引擎
在 Web 編程中,模板引擎用于聚合數據和模板并生成最終的 HTML 文檔,處理器調用模板引擎來完成這一工作并將 HTML 文檔作為響應實體發(fā)送給客戶端:

雖然模板引擎沒有統(tǒng)一的標準,甚至不同的模板引擎提供的功能特性也是天差地別,但是仍然可以劃分為兩種不同的類型:
無業(yè)務邏輯:數據通過指定占位符替換,模板中不包含業(yè)務邏輯,所有業(yè)務邏輯都在處理器中完成,這樣做的好處是將業(yè)務邏輯和數據渲染很好的隔離開。
嵌入業(yè)務邏輯:在視圖模板中嵌入業(yè)務邏輯,這使得視圖模板的功能非常強大,但是這樣一來,也使得代碼維護非常困難。
我們傾向于無業(yè)務邏輯嵌入的模板引擎,這樣的視圖模板性能更好,可維護性更好,但是絕對的無業(yè)務邏輯嵌入也是做不到的(比如一些簡單的條件判斷和循環(huán)),大部分時候這取決于業(yè)務開發(fā)團隊的約定,盡量不要在視圖模板中編寫業(yè)務邏輯代碼。
PHP 誕生之初就是一個將業(yè)務邏輯和 HTML 視圖混為一體的腳本語言,不過現在的 PHP 腳本中已經很少看到 HTML 代碼了,這是 PHP 框架的功勞,比如 Laravel、Yii,PHP 自身作為一個模板引擎,現在已經衍生出獨立的模板引擎,比如 Smarty、Blade。
實際上,大部分模板引擎都是介于以上兩種類型之間,只能說離誰更近一些。
Go 語言官方提供的模板引擎 text/template 和 html/template 也是這樣的混合物。
2、Go 模板引擎
Go 模板引擎都是在處理器中觸發(fā),指定要解析的模板文件,并傳入待渲染的數據,最后返回由模板引擎最終生成的 HTML 作為 HTTP 響應發(fā)送給客戶端:

Go 模板都是文本文檔,在 Web 應用中,通常是 HTML 文檔,其中包含了嵌入的命令。這些文檔會被 Go 模板引擎解析和執(zhí)行,生成另外的文本片段(替換完命令和數據)。
Go 標準庫提供了 text/template 庫用于解析任意類型的文本格式模板,以及 html/template 庫用于解析并處理 HTML 格式模板。
在這些模板中,命令以 {{ 和 }} 包裹(實際上,這些界定符可以通過程序進行修改),下面我們看一段簡單的模板代碼 tmpl.html:
<html?lang="en">
????<head>
????????<meta?http-equiv="Content-Type"?content="text/html;?charset=utf-8">
????????<title>Go?Web?Programmingtitle>
????head>
????<body>
????????{{?.?}}
????body>
html>
模板文件中的內容必須是可讀的文本格式,但是擴展名可以隨意,在這里由于最終生成的是 HTML 文檔,所以我們使用了 .html 擴展名(tmpl.html)。
{{ . }} 中的 . 就是一個命令,用于在模板執(zhí)行時替換從處理器傳入的變量。
使用 Go 模板引擎通常包括以下兩個步驟:
解析文本模板源,可以是表單字符串、或者模板文件,用于創(chuàng)建解析后的模板結構體。
執(zhí)行解析后的模板,傳遞
ResponseWriter和變量數據,這樣一來,模板引擎就可以基于模板和數據生成最終的 HTML 并將其傳遞給ResponseWriter發(fā)送給客戶端。
下面是服務端處理器調用模版引擎渲染上述模板代碼的示例:
package?main
import?(
????"html/template"
????"net/http"
)
func?process(w?http.ResponseWriter,?r?*http.Request)??{
????t,?_?:=?template.ParseFiles("tmpl.html")?//?解析模板,返回?Template
????t.Execute(w,?"Hello?World!")??//?執(zhí)行模板,并將其傳遞給?w
}
func?main()??{
????http.HandleFunc("/template",?process)
????http.ListenAndServe(":8080",?nil)
}
運行上述代碼啟動服務器,在終端窗口通過 curl 請求 /template 路由,返回結果如下:

表明 HTML 模板解析渲染成功,對應的 {{ . }} 也別替換成 Hello World!。
解析模板
在上面的示例代碼中,我們調用了 ParseFiles 方法解析模板文件并創(chuàng)建稍后執(zhí)行的解析后的 Template。其底層分為兩步,它可以接收一個或多個模板文件名稱,傳入多個模板文件名的時候,會以第一個文件名作為模板名稱,后續(xù)其它模板通常是第一個模板或者其他模板嵌套的子模板。
此外,我們還可以通過 ParseGlob 方法解析模板,該方法傳入的參數是模式匹配串,而不是文件名稱:
t,?_?:=?template.ParseFiles("tmpl.html")
t,?_?:=?template.ParseGlob("*.html")
如果當前路徑只有一個 tmpl.html 模板文件,上述代碼的效果是一樣的。
除了解析文件之外,還支持解析字符串,實際上,所有解析方法最終調用的都是 Parse 方法:
package?main
import?(
????"html/template"
????"net/http"
)
func?parseFiles(w?http.ResponseWriter,?r?*http.Request)??{
????t,?_?:=?template.ParseFiles("tmpl.html")
????t.Execute(w,?"Hello?World!")
}
func?parseString(w?http.ResponseWriter,?r?*http.Request)??{
????tmpl?:=?`?
????????
????????????
????????????Go?Web?Programming
????????
????????
????????????{{?.?}}
?????????
????`
????t?:=?template.New("tmpl.html")
????t.Parse(tmpl)
????t.Execute(w,?"Hello?World!")
}
func?main()??{
????http.HandleFunc("/template",?parseFiles)
????http.HandleFunc("/string",?parseFiles)
????http.ListenAndServe(":8080",?nil)
}
上面 parseString 方法和 parseFiles 方法實現的效果是一樣的,實際上 ?template.ParseFiles("tmpl.html") 底層調用的代碼正是:
t?:=?template.New(filename)
t.Parse(string)?//?string?就是讀取傳入?file?的文本內容
在上面的代碼中,我們忽略了 template.ParseFiles 返回的錯誤信息,不過,Go 官方建議我們對這個錯誤進行處理,為此,Go 還提供了更簡潔的方式來處理模板解析過程中出現的錯誤:
t?:=?template.Must(template.ParseFiles("tmpl.html"))
這種情況下,如果解析模板過程中出現問題,則拋出 panic(在 Go 語言中,panic 有點類似其它語言的異常,當函數內拋出 panic 時,會一直上溯到 main 入口,然后崩潰)。
執(zhí)行模板
如果只解析一個模板文件的話,使用 Execute 方法就夠了,如果要解析多個模板文件,也可以使用 Execute 方法,這個時候,會使用傳入模板文件的第一個作為模板名稱,并將其作為入口模板,如果要指定其它模板作為入口模板(或者稱之為布局模板),需要調用 ExecuteTemplate 方法并將模板名作為第二個參數傳遞進去:
t,?_?:=?template.ParseFiles("t1.html",?"t2.html")
t.Execute(w,?"Hello?World!")
t.ExecuteTemplate(w,?"t1.html",?"Hello?World!")
上面的 t.Execute 和 t.ExecuteTemplate 執(zhí)行結果等效,如果你要從 t2.html 開始解析,需要這樣指定入口文件:
t.ExecuteTemplate(w,?"t2.html",?"Hello?World!")
(全文完)
推薦閱讀
站長 polarisxu
自己的原創(chuàng)文章
不限于 Go 技術
職場和創(chuàng)業(yè)經驗
Go語言中文網
每天為你
分享 Go 知識
Go愛好者值得關注
