场景
现在的项目,基本都是前后端分离,后端只要提供Json等格式的数据就行。在这个背景下,模板渲染这个功能备受冷落,很少会在项目中用到。
虽然在 http 服务中,模板解析不常用,但日常开发中,巧妙利用模板生成代码,能使我们开发事半功倍。比如:
- 使用模板初始化项目。比如我们每次新建一个 http 服务,可能都需要 promethue 监控、日志等模块。每次都实现一遍不现实,(或者 copy 别的项目),我们可以写好模板,支持自定义项目名,初始化新项目。
- 生成代码。比如之前文章提到的 mockery,就是解析 interface 的语法树,利用模板生成 Mock 对象。
text/template 包
基本用法
text/templatehtml/template
text/template 用法很简单:
func main() { // 要注入的变量 type Inventory struct { Material string Count uint } sweaters := Inventory{"wool", 17} // 模板内容, {{.xxx}} 格式的都会被注入的变量替换 text := `{{.Count}} items are made of {{.Material}}` TestTemplate(text, sweaters) } func TestTemplate(text string, data interface{}) { // 初始化,解析 tmpl, err := template.New("test").Parse(text) if err != nil { panic(err) } // 输出到 os.Stdout err = tmpl.Execute(os.Stdout, data) if err != nil { panic(err) } }
结果输出如下:
17 items are made of wool
Execute
// 第一个参数是输出的接口,第二个参数是要注入的数据 func (t *Template) Execute(wr io.Writer, data interface{}) error
第二个参数 data 是 interface{},类型不限,可以是:
- 结构体,属性在模板中用 {{.Field}} 表示。
- map,value 在模板中用 {{.Key}} 表示。
- 其他简单类型(int、string等),在模板中用{{.}}表示。
// data 为 map m := map[string]interface{}{"Material": "wool", "Count": 17} TestTemplate(`{{.Count}} items are made of {{.Material}}`, m) // 输出:17 items are made of wool // data 为 int,{{.}} 代表注入的变量 TestTemplate(`{{.}} items`, 17) // 输出:17 items
template.Must
tmpl, err := template.New("test").Parse(text) if err != nil { panic(err) }
等价于:
tmpl := template.Must(template.New("test").Parse(txt))
Parsetext
模板语法
{{xxx}}{{}}
空白字符
{{--}}
d := struct{ Name string }{"Neil"} TestTemplate(`name = {{.Name}} ;`, d) // 输出:name = Neil ; // 删除掉 .Name 左边的空格 TestTemplate(`name = {{- .Name}} ;`, d) // 输出:name =Neil ; // 删除掉 .Name 右边的空格 TestTemplate(`name = {{.Name -}} ;`, d) // 输出:name = Neil; // 删除掉 .Name 两边的空格 TestTemplate(`name = {{- .Name -}} ;`, d) // 输出:name =Neil;
常用 Action
{{/*xxxx*/}}
text := `{{/*this is a comment*/}} name : {{.}} ` TestTemplate(text, "Neil") // 输出: // name : Neil
遍历,可以使用 range 关键字。遍历的变量只能是 slice、array、map 或者 channel。
{{.}}.MapContent
d1 := struct{ MapContent []string }{MapContent: []string{"neil", "garmen", "ray"}} text = "{{range .MapContent}}{{.}} {{end}}" TestTemplate(text, d1) // 输出: // neil garmen ray
.d1d1{{$.}}
另外也可以使用自定义变量来遍历:
text = "{{range $i,$v := .MapContent}}{{$i}}=>{{$v}} {{end}}" TestTemplate(text, d1) // 输出 // 0=>neil 1=>garmen 2=>ray
if-else,变量为零值,或者空 slice、array、map,就相当于是 false。
text = `{{if .Name}}emtpy{{else}}not empty{{end}}` d = struct{ Name string }{"Neil"} TestTemplate(text, d) // 输出: empty
if 还可以配合 and、or、not 使用:
// .condition1 && .condition2 if and .condition1 .condition2 // .condition1 || .condition2 if or .condition1 .condition2 // !.condition if not .condition
{{.}}{{.}}
text = "{{with .Name}}{{.}}{{else}}empty{{end}}" d = struct{ Name string }{"Neil"} TestTemplate(text, d) // 输出: Neil
definetemplate
text = `{{define "T1"}}ONE{{end}} {{define "T2"}}TWO{{end}} {{define "T3"}}{{template "T1"}} {{template "T2"}}{{end}} {{template "T3"}}` TestTemplate(text, nil) // 输出: ONE TWO
函数
andornot
text = `{{index .MapContent 1}}` d1 = struct{ MapContent []string }{MapContent: []string{"neil", "garmen", "ray"}} TestTemplate(text, d1) // 输出:garmen
{{func1 args | func2 }}
总结
text/template 功能挺多,本文只是大致介绍一下,详情还得移步官网