深入淺出標準庫 text/template 包
go 語言標準庫 text/template 在 web 編程項目中經常用到,本文詳細介紹了這個包的用法。
官方定義:
Package template implements data-driven templates for generating textual output.
template 包是數據驅動的文本輸出模板,其實就是在寫好的模板中填充數據。
模板
什么是模板?
下面是一個簡單的模板示例:
{{ 和 }} 中間的句號?.
?代表傳入模板的數據,根據傳入的數據不同渲染不同的內容。
.
?可以代表 go 語言中的任何類型,如結構體、哈希等。
至于 {{ 和 }} 包裹的內容統稱為 action,分為兩種類型:
-
數據求值(data evaluations) -
控制結構(control structures)
action 求值的結果會直接復制到模板中,控制結構和我們寫 Go 程序差不多,也是條件語句、循環語句、變量、函數調用等等。..
將模板成功解析(Parse)后,可以安全地在并發環境中使用,如果輸出到同一個?io.Writer
?數據可能會重疊(因為不能保證并發執行的先后順序)。
Actions
模板中的 action 并不多,我們一個一個看。
注釋
裁剪空格
文本輸出
pipeline 代表的數據會產生與調用?fmt.Print
?函數類似的輸出,例如整數類型的 3 會轉換成字符串 "3" 輸出。
條件語句
如果 pipeline 的值為空,不會輸出 T1,除此之外 T1 都會被輸出。
空值有 false、0、任意 nil 指針、接口值、數組、切片、字典和空字符串?""
(長度為 0 的字符串)。
循環語句
pipeline 的值必須是數組、切片、字典和通道中的一種,即可迭代類型的值,根據值的長度輸出多個 T1。
define
定義命名為 name 的模板。
template
引用命名為 name 的模板。
block
block 的語義是如果有命名為 name 的模板,就引用過來執行,如果沒有命名為 name 的模板,就是執行自己定義的內容。
也就是多做了一步模板是否存在的判斷,根據這個結果渲染不同的內容。
with
with 創建一個新的上下文環境,在此環境中的?.
?與外面的?.
?無關。
參數
參數的值有多種表現形式,可以求值任何類型,包括函數、指針(指針會自動間接取值到原始的值):
-
布爾、字符串、字符、浮點數、復數的行為和 Go 類似 -
關鍵字? nil
?代表 go 語言中的?nil
-
字符句號 . 代表值的結果 -
以 $ 字符開頭的變量則為變量對應的值 -
結構體的字段表示為? .Field
,結果是 Field 的值,支持鏈式調用?.Field1.Field2
-
字典的 key 表示為? .Key
?結果是 Key 對應的值 -
如果是結構體的方法集中的方法 .Method 結果是方法調用后返回的值(The result is the value of invoking the method with dot as the receiver)** -
方法要么只有一個任意類型的返回值要么第二個返回值為 error,不能再多了,如果 error 不為 nil,會直接報錯,停止模板渲染 -
方法調用的結果可以繼續鏈式調用? .Field1.Key1.Method1.Field2.Key2.Method2
-
聲明變量方法集也可以調用? $x.Method1.Field
-
用括號將調用分組? print (.Func1 arg1) (.Func2 arg2)
?或?(.StructValuedMethod "arg").Field
-
這里最難懂的可能就是函數被調用的方式,如果訪問結構體方法集中的函數和字段中的函數,此時的行為有什么不同?
寫個 demo 測一下:
可以得出結論:如果函數是結構體中的函數字段,該函數不會自動調用,只能使用內置函數?call
?調用。
如果函數是結構體方法集中的方法,會自動調用該方法,并且會將返回值賦值給?.
,如果函數返回新的結構體、map,可以繼續鏈式調用。
變量
action 中的 pipeline 可以初始化變量存儲結果,語法也很簡單:
此時,這個 action 聲明了一個變量而沒有產生任何輸出。
range 循環可以聲明兩個變量:
在 if、with 和 range 中,變量的作用域拓展到 {{ end }} 所在的位置。
如果不是控制結構,聲明的變量的作用域會擴展到整個模板。
例如在模板開始時聲明變量:
在渲染開始的時候,$
?變量會被替換成?.
?開頭的值,例如?$pages
?會被替換成?.pagenation.Pages
。所以在模板間的相互引用不會傳遞變量,變量只在某個特定的作用域中產生作用。
函數
模板渲染時會在兩個地方查找函數:
-
自定義的函數 map -
全局函數 map,這些函數是模板內置的
自定義函數使用?func (t *Template) Funcs(funcMap FuncMap) *Template
?注冊。
全局函數列表:
and
返回參數之間 and 布爾操作的結果,其實就是 JavaScript 中的邏輯操作符?&&
,返回第一個能轉換成 false 的值,在 Go 中就是零值,如果都為 true 返回最后一個值。
or
邏輯操作符?||
,返回第一個能轉換成 true 的值,在 Go 中就是非零值,如果都為 false 返回最后一個值。
call
返回調用第一個函數參數的結果,函數必須有一個或兩個回值(第二個返回值必須是 error,如果值不為 nil 會停止模板渲染)
html
返回轉義后的 HTML 字符串,這個函數不能在?html/template
?中使用。
js
返回轉義后的 JavaScript 字符串。
index
在第一個參數是 array、slice、map 時使用,返回對應下標的值。
index x 1 2 3
?等于?x[1][2][3]
。
len
返回復合類型的長度。
not
返回布爾類型參數的相反值。
等于?fmt.Sprint
。
printf
等于?fmt.Sprintf
。
println
等于?fmt.Sprintln
。
urlquery
對字符串進行 url Query 轉義,不能在?html/template
?包中使用。
從源碼可以看到這個函數直接調用?url.QueryEscape
?對字符串進行轉義,并沒有什么神秘的。
比較函數
-
eq
: == -
ge
: >= -
gt
: > -
le
: <= -
lt
: < -
ne
: !=
分析兩個源碼:
eq 先判斷接口類型是否相等,然后判斷值是否相等,沒什么特殊的地方。
ne 更是簡單的調用 eq,然后取反。
ge、gt、le、lt 與 eq 類似,先判斷類型,然后判斷大小。
嵌套模板
下面是一個更復雜的例子:
ExecuteTemplate
?指定的名字就是模板文件中?define "name"
?的 name。
總結
Parse
?系列函數初始化的?Template
?類型實例。
Execute
?系列函數則將數據傳遞給模板渲染最終的字符串。
模板本質上就是?Parse
?函數加載多個文件到一個?Tempalte
?類型實例中,解析文件中的 define 關鍵字注冊命名模板,命名模板之間可以使用?template
?互相引用,Execute
?傳入對應的數據渲染。
轉自:
juejin.cn/post/6844903762901860360