Go-知识 模板
Go-知识 模板
- 1. 介绍
- 2. Text/template 包
- 3. Html/template 包
- 4. 模板语法
- 4.1 模板标签
- 4.2 添加注释
- 4.3 访问变量
- 4.4 访问方法
- 4.5 模板变量
- 4.6 访问函数
- 4.7 数据渲染
- 4.8 条件判断
- 4.9 循环遍历
- 4.10 嵌入子模板
- 4.11 局部变量
- 4.12 输出字符串
- 4.13 预定义的全局函数
- 4.14 比较函数
1. 介绍
fmt.Printf
可以做到格式化输出,这对于简单的例子已经足够,但是有时候还需要更加复杂的输出格式,甚至需要将格式与代码分离开来。这个时候就可以使用模板(Template)。
2. Text/template 包
text/template
包提供了处理文字模板与数据的功能,模板引擎。
所谓模板引擎,就是将模板和数据进行渲染的输出格式化后的字符串程序。
使用模板引擎分为三步:
- 创建模板对象
- 加载模板
- 执行渲染模板
比如如下例子:
package gostudy
import (
"os"
"testing"
"text/template"
)
func TestTe(t *testing.T) {
templ := `
{{range.}}--------------------------------------
Name: {{.Name}}
Age: {{.Age}}
{{end}}
`
tp := template.Must(template.New("templ").Parse(templ))
type Person struct {
Name string
Age int
}
persons := []Person{
{"gw", 18},
{"lx", 20},
{"ly", 21},
}
if err := tp.Execute(os.Stdout, persons); err != nil {
t.Log(err)
}
}
执行结果如下
在代码中, templ
就是一段模板文字,然后使用程序中的数据,渲染模板,填充模板中的占位,最后得到完整的输出。
除了将模板文字在程序中写死,也可以将模板与程序分离,也就是格式和数据分离,使用不同的文件存储
首先创建一个文件用于存储模板文字,后缀可以自定义
然后在程序中直接加载模板文件
func TestTeTxt(t *testing.T) {
tp := template.Must(template.ParseFiles("./name_age.tpl"))
type Person struct {
Name string
Age int
}
persons := []Person{
{"gw", 18},
{"lx", 20},
{"ly", 21},
}
if err := tp.Execute(os.Stdout, persons); err != nil {
t.Log(err)
}
}
执行结果与之前完全相同
如果要加载多个模板文件,有两种方式:枚举方式,正则方式 以及目录方式
枚举方式 tp := template.Must(template.ParseFiles("./name_age1.tpl", "./name_age2.tpl","./name_age3.tpl"))
正则方式 tp := template.Must(template.ParseGlob("./*.tpl"))
目录方式
files, err := filepath.Glob("./*.html.tmpl")
if err != nil {
log.Fatalf("Error while globbing files: %v", err)
}
tp := template.Must(template.ParseFS(os.DirFS("./)"), files...))
template.Must
主要是检测模板是否正确。
3. Html/template 包
和 text/template
类似,html/template
主要提供支持 HTML 模板的功能,使用方法差不多。
创建一个index.html.tmpl
文件
模板内容如下
<!doctype html>
<head>
<meta charset="UTF-8">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Go</title>
</head>
<body>
{{.}}
</body>
</html>
然后创建一个web服务端
func TestHt(t *testing.T) {
tp := template.Must(template.ParseFiles("./index.html.tmpl"))
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
tp.Execute(writer, "Hello")
})
http.ListenAndServe(":8080", nil)
}
访问
在多模板的时候,可以定义模板的名字,然后在执行的时候,指定模板渲染
比如
使用 {{ define "index"}}
定义了模板的name
然后加载模板的时候,加载全部的模板,指定index1 执行
func TestHt(t *testing.T) {
files, err := filepath.Glob("./*.html.tmpl")
if err != nil {
log.Fatalf("Error while globbing files: %v", err)
}
tp := template.Must(template.ParseFS(os.DirFS("./"), files...))
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
tp.ExecuteTemplate(writer, "index1", "Hello")
})
http.ListenAndServe(":8080", nil)
}
执行并访问
同时在模板文件之间可以进行嵌套
比如如下模板
然后修改index1.html.tmpl
重启web
4. 模板语法
4.1 模板标签
{{
和 }}
就是模板标签,中间是模板的语法内容。
4.2 添加注释
{{/* 注释 */}}
, 使用 {{/*
和 */}}
包含注释的内容。
2. 注释 `{{/* 注释 */}}`: <br/>
{{/* 这是一条注释 */}}
<br/>
4.3 访问变量
{{.}}
此标签输出当前对象值。
{{.Name}}
表示输出对象中字段或方法名为 Name
的值。
需要注意,如果方法定义为
func (p Person)Name() string
那么,在渲染模板的时候,一定是值对象。如果方法定义为
func (p *Person)Name() string
那么,在渲染模板时,一定是指针对象。
如果 Name
是匿名字段,那么还可以继续访问内部字段,比如 {{.Name.First}}
。
如果存在一个方法Say
,并且是 Name
的方法。
假设Say
返回对象,那么可以继续访问{{.Name.Say.Field}}
{{.F1.F2.F3}}
,F 可以是方法也可以是结构体。(要小心空指针)
1. 输出当前对象的值 `{{ . }}` : <br/>
{{ . }}
<br/>
3. 变量: <br/>
3.1 当前对象 `\{\{ . \}\}` : {{ . }} <br/>
属性 NameStr : {{ .NameStr }} <br/>
3.2 方法 `\{\{ .Name \}\}` : {{ .Name }} <br/>
3.3 定义变量 `\{\{ $x := "test" \}\}` : {{ $x := "test" }} <br/>
3.4 访问变量 `\{\{ $x \}\}` : {{ $x }} <br/>
3.5 定义变量 n , 接收当前对象的值,然后传递给 ForName 函数,返回 对象并打印输出: <br/>
`\{\{ $n := .NameStr \}\}`,`\{\{ .ForName $n \}\}` <br/>
实际上等价于 `\{\{ .ForName \}\}` <br/>
{{ $n := .NameStr }} <br/>
{{ $n }} <br/>
{{ .ForName $n }} <br/>
{{ .ForName .NameStr }} <br/>
4.4 访问方法
{{.Method param1 param2 param3}}
调用方法 Method,后面的 param是调用参数
4.5 模板变量
在模板中定义变量,变量名称用字母和数据组成,并加上 $
前缀,采用简短赋值。
比如 {{ $x := "OK" }}
, {{ $y := "yes"}}
访问定义的模板变量
{{ $x }}
用于输出在模板中定义的名称为 x 的变量,当 定义的变量是个结构体的时候,可以连续访问
4.6 访问函数
- 函数:在 Go 中,函数是独立的,必须显式地注册到模板中,以便在模板中使用。
- 方法:方法是与特定类型(如结构体)关联的。当你在模板中使用结构体的实例时,模板引擎会自动识别该实例的方法。
在模板中要使用函数,必须先注册,不注册的话,是不能使用的
{{ FuncName1 }}
调用标签名字为 FuncName1 的函数,等价于执行 FuncName1()
{{ FuncName1 param1 param2 ..}}
调用带有参数的函数
{{ FuncName1 . }}
等价于 FuncName1(this)
,这里的 this 取决于传递给模板渲染时的数据。
{{ .|FuncName1 }}
和 {{ FuncName1 . }}
3.6 访问函数 `\{\{ SayName .NameStr \}\}`: {{ SayName .NameStr }} <br/>
3.7 访问函数 `\{\{ .|Say \}\}` : {{ .|Say }} <br/>
访问函数 `\{\{ Say . \}\}` : {{ Say . }} <br/>
4.7 数据渲染
在模板渲染的时候,如果想使用复杂的数据结构或者需要渲染很多数据,那么可以使用复杂struct 或者map进行传递。
如果使用map
data := map[string]interface{}{
"NameStr": "xiaomei",
"Age": 18,
"Country": "China",
}
模板
<h1>{{ SayHello .NameStr }}!</h1>
<p>Age: {{ .Age }}</p>
<p>Country: {{ .Country }}</p>
使用struct通常更具可读性和类型安全,而使用 map
则提供了更大的灵活性。
4.8 条件判断
{{ if condition }} T1 {{ end }}
结构为 {{ if ... }} ... {{ end }}
,类似go里面单个 if
{{ if condition }} T1 {{ else }} T2 {{ end }}
结构为{{ if ... }} ... {{ else }} ... {{ end }}
,类似 go 里面的 if-else
{{ if condition }} T1 {{ else if con2}} T2 ... {{ else }} Tn {{ end }}
类似go 里面的多if分支
if 后面可以是一个条件表达式,条件可以是调用函数,方法等等,也可以是一个字符串变量或者布尔值变量。
当是字符串变量时,空字符串为 false,非空为true。
4. 条件 <br/>
Sex = {{ .Sex }}, Age = {{ .Age }} <br/>
单个 if : {{ if eq .Sex "woman" }}
女
{{ else }}
男
{{ end }}
<br/>
多个 if :
{{ if and (eq .Sex "woman") (le .Age 18 ) }}
少女
{{ else if and (eq .Sex "man") (le .Age 18 ) }}
少男
{{ else }}
其他
{{ end }}
4.9 循环遍历
{{ range $k,$v := .Var }} T {{ end }}
range … end 结构内部如果要使用外部的变量,需要在外部变量的名字前加 $
{{ range .var }} {{ . }} {{ end }}
将遍历值直接展示出来
{{ range condition }} T {{ else }} TT {{ end }}
当没有可遍历的值的时候,执行 else 部分
5. 循环遍历 <br/>
遍历数组直接输出<br/>
{{ range .Msg }}
{{ . }} ,
{{ end }}
<br/>
遍历map输出<br/>
{{ range .MsgMap }}
{{ . }}
{{ end }}
<br/>
遍历输出 k,v <br/>
{{ range $k,$v := .MsgMap }}
( {{ $k }} , {{ $v }} ) <br/>
{{ end }}
<br/>
定义外部变量 {{ $t := "say:" }} <br/>
使用外部变量 <br/>
{{ range $k,$v := .MsgMap }}
{{ $t }} : {{ $k }} => {{ $v }} <br/>
{{ end }}
<br/>
带有条件的循环遍历<br/>
{{ range $k, $v := .MsgMap }}
{{ if le $v 3 }}
{{ $k }} => {{ $v }} <br/>
{{ else }}
({{$k}},{{$v}})<br/>
{{ end }}
{{ end }}
<br/>
输出
4.10 嵌入子模板
{{ template "name"}}
嵌入名称为 “name” 的子模板。 使用前必须使用 {{ define "name"}}
… {{ end }}
进行定义
{{ define "div" }}
<div>
<b> World </b>
</div>
{{ end }}
6. 子模板 <br/>
{{ template "div" }}
子模板可以嵌套多次,嵌套多个
4.11 局部变量
{{ with ...}} T {{ end }}
将值赋值给标签内部的 .
。
{{ with ...}} T {{ else }} TT {{end}}
如果值为空,执行 else
7. 局部变量 <br/>
{{ with .NameStr }}
nameStr = {{ . }}<br/>
{{ end }}
<br/>
带有 else 的 with <br/>
{{ with "" }}
不空
{{ else }}
空
{{ end }}
<br/>
4.12 输出字符串
{{ "\"output\""}}
转义输出
` 可以使字符串原样输出
{{ pintf "%q" "output"}}
函数调用输出 等价于 printf("%q", "output")
{{ "output"|printf "%q"}}
另一种调用方式 printf("%q","output")
{{ printf "%q" (print "out" "put")}}
多层调用 printf("%q", print("out","put"))
{{ "put" | print "%s%s" "out" | printf "%q"}}
另一种写法printf("%q", print("%s%s", "out","put"))
{{ "output" | prinf "%s" | printf "%q"}}
等价于 printf("%q", printf("%s", "output"))
{{ with "output"}} {{ printf "%q" .}} {{ end }}
使用 .
的with 写法
{{ with $x := "output" | printf "%q"}} {{ $x }} {{ end }}
使用通道的with写法
{{ with $x := "output"}} {{ $x | printf "%q"}} {{ end }}
另一种写法
8. 字符串<br/>
{{ "\"output\"" }} <br/>
{{ `"output"` }} <br/>
{{ `{{ printf "%q" "output" }}` }} {{ printf "%q" "output" }}<br/>
{{ with "output" }} {{ . | printf "%q" }} {{ end }} <br/>
4.13 预定义的全局函数
{{ and x y}}
如果 x 为真,返回 y,否则返回 x
{{ or x y }}
如果 x 为真,返回x ,否则返回 y
{{ call func param1 param2 ...}}
调用函数,函数返回值限定为 1个或者2个(第二个必须是 error)
如果传递的参数与函数定义的不匹配,或者返回的 error 不为 nil ,停止执行
{{html}}
转义文本中的 html 标签
{{ index map 1 2 3}}
返回 index 后面的第一个参数的某个索引对应的元素值,其余参数作为索引值。必须是 map,数组,slice
{{ js }}
返回用 javascript 的 escape 处理后的文本
{{ len x }}
返回长度
{{ not x}}
取反
{{ print }}
fmt.Sprint 的别名
{{ printf }}
fmt.Sprintf 的别名
{{ println }}
fmt.Sprintln的别名
{{ urlquery }}
在URL查询中嵌入到形参中的文本转义,类似 urlencode
9. 预定义<br/>
<p>and: {{ and true "Y" }}</p>
<p>or: {{ or false "Y" }}</p>
<p>len: {{ len .Msg }}</p>
<p>not: {{ not false }}</p>
<p>print: {{ print "Hello, " "World!" }}</p>
<p>printf: {{ printf "Hello, %s!" "World" }}</p>
<p>println: {{ println "Hello," "World!" }}</p>
<p>index: {{ index .MsgMap "two" }}</p>
<p>call: {{ call .SayN "xiaomei" }}</p>
4.14 比较函数
{{ eq arg1 arg2}}
:=> arg1 == arg2
{{ ne arg1 arg2 }}
:=> arg1 != arg2
{{ lt arg1 arg2 }}
:=> arg1 < arg2
{{ le arg1 arg2 }}
:=> arg1 <= arg2
{{ gt arg1 arg2 }}
:=> arg1 > arg2
{{ ge arg1 arg2 }}
:=> arg1 >= arg2
比较函数对任何零值返回 false ,非零值返回 true 。
比较函数每次只接受两个参数
{{ eq arg1 arg2 arg3 arg4}}
等价于 arg1 == arg2 || arg1 == arg3 || arg1 == arg4