关于html/template
html/template中实现了数据驱动的模板,与text/template具有相同的接口。前者用于输出THTML,或者用户text。通过关于html/template包可以有效防止代码注入,破坏HTML的内容。
模板与渲染有什么作用呢?
在前端和后端分离的Web项目中,我们经常要将后端的数据应用在前端的HTML页面中。那通过发送响应报文,然后前端去修改不就好了。设想每次都去修改的话,是不是特别的不方便。所以模板渲染的作用就显而易见了。比如,很多HTML页面的布局都差不多,但是需要展示的数据却不相同,这时候我们就可以通过模板的方式动态的更新HTML页面中的数据。(如何去调用这些数据,其实无非就是通过某种规定的语法去获取数据)
注:在大多数的web框架中都支持模板引擎
模板是什么呢?就是HTML文件而已
渲染时什么呢?就是用后台的数据(此时此刻我想在HTML页面中展示的数据)去替换HTML模板中的有模板语法规定的地方,从而将数据实时的显示在模板文件中。
Go模板引擎的相关规定
1.模板文件必须使用utf-8编码方式,文件后缀名最好使用 .tmpl 后者 .tpl (当然使用.html也可以)
2.模板文件中通过 {{}} 来表示需要获取的内容。(也可以自己定义)
3.通过 . 获取数据 .fieldName来获取结构体中的字段
GO模板引擎的使用
GO模板引擎的使用分为三个步骤:定义模板、解析模板、渲染模板
定义模板:
定义模板就是用一些规定的语法规则去写HTML文件
- 语法规则
1.语法规则(语句)都要写在{{XXX}}中【XXX为语法规则】
2.{{.}} 中的 . 表示传过来的内容
3.{{/* XXXX */}}中的 /* */表示注释,可以多行,不能嵌套,必需紧跟着{{和}}
4.GO模板中可以使用 | 来链接多个命令,前面的命令会将运算结果传递给后一个命令
5. 变量,通过$来定义变量
6.去掉前后空格{{- XXX -}} -必须紧贴 {{ 和 }}
7.条件判断
{{if 条件}}内容1{{end}} {{if 条件}}内容1{{else}}内容2{{end}} {{if 条件1}}内容1{{else if 条件2}}}内容2{{end}} {{if 条件1}}内容1{{else if 条件2}}}内容2{{else}}内容3{{end}}
8.range(遍历) 遍历的对象必须是数组、切片、字段典、通道
{{range pipeline}} 内容 {{end}} {{range pipeline}} 内容1 {{else}} 内容2{{end}}
9.with
{{with pipeline}} T1 {{end}} {{with pipeline}} T1 {{else}}T2{{end}}
注:执行模板时,一般先从函数模板字典查找,然后再从全局函数字典中查找。不建议在模板内定义函数,而是将函数添加到模板函数中。
10.预定义全局函数(大概来了解下,需要用的时候再回来查)
and 函数返回它的第一个empty参数或者最后一个参数; 就是说"and x y"等价于"if x then y else x";所有参数都会执行; or 返回第一个非empty参数或者最后一个参数; 亦即"or x y"等价于"if x then x else y";所有参数都会执行; not 返回它的单个参数的布尔值的否定 len 返回它的参数的整数类型长度 index 执行结果为第一个参数以剩下的参数为索引/键指向的值; 如"index x 1 2 3"返回x[1][2][3]的值;每个被索引的主体必须是数组、切片或者字典。 print 即fmt.Sprint printf 即fmt.Sprintf println 即fmt.Sprintln html 返回与其参数的文本表示形式等效的转义HTML。 这个函数在html/template中不可用。 urlquery 以适合嵌入到网址查询中的形式返回其参数的文本表示的转义值。 这个函数在html/template中不可用。 js 返回与其参数的文本表示形式等效的转义JavaScript。 call 执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数; 如"call .X.Y 1 2"等价于go语言里的dot.X.Y(1, 2); 其中Y是函数类型的字段或者字典的值,或者其他类似情况; call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同); 该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型; 如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;
11.比较函数(布尔函数)
注1:布尔函数会将任何类型的零值视为false,非零值视为true
注2:只有相同类型可以相互比较
eq 如果arg1 == arg2则返回真 ne 如果arg1 != arg2则返回真 lt 如果arg1 < arg2则返回真 le 如果arg1 <= arg2则返回真 gt 如果arg1 > arg2则返回真 ge 如果arg1 >= arg2则返回真
12.自定义函数
Go支持自定义模板函数,并且自定义函数一般不写在模板文件中(即不写在{{}}中)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>干饭小白</title> </head> <body> {{tmpFunc .}} </body> </html>
package main import ( "fmt" "html/template" "io/ioutil" "net/http" ) //测试:模板函数自定义函数 func MyHandler(w http.ResponseWriter, r *http.Request) { //读取模板文件中的内容 temByte, err := ioutil.ReadFile("./index.tmpl") if err != nil { fmt.Println("read tmpl err=", err) return } //自定义一个模板函数 我们为了方便定义一个匿名函数 tmpFunc := func(arg string) string { return arg + "love you" } //创建一个模板实例 tem := template.New("index") //添加自定义函数 map[key]interface{}-->key方便在模板函数中找到函数入口 tmp2 := tem.Funcs(template.FuncMap{"tmpFunc": tmpFunc}) //解析模板(解析模板函数中的内容) tmp2, err = tmp2.Parse(string(temByte)) if err != nil { fmt.Println("Parse err=", err) return } //执行模板 tmp2.Execute(w, "chen") } func main() { http.HandleFunc("/", MyHandler) http.ListenAndServe(":9090", nil) }
13.嵌套template
GO模板支持在一个模板文件中嵌套另一个模板文件
支持直接在同一个文件中:
{{define "待嵌套文件名"}}
XXXXX
{{end}}
package main import ( "fmt" "html/template" "net/http" ) type User struct { Id int Name string Password string } func temHandler(w http.ResponseWriter, r *http.Request) { //定义模板 //解析模板 temp, err := template.ParseFiles("t1.tmpl", "t2.tmpl") if err != nil { fmt.Println("模板解析失败") return } //执行模板 //搞个结构体吧 u := &User{ Id: 22, Name: "干饭小白", } temp.Execute(w, u) } func main() { http.HandleFunc("/temp", temHandler) http.ListenAndServe(":9090", nil) }
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>干饭小白</title> </head> <body> <h2>嵌套模板t2</h2> <hr> {{.Name}} <!-- define嵌套 --> <h3>define嵌套</h3> {{template "t3.tmpl"}} <hr> <!-- 跨文件嵌套 --> <h3>跨文件嵌套</h3> {{template "t2.tmpl"}} </body> </html> {{define "t3.tmpl"}} <ol> <li>吃饭</li> <li>干饭</li> <li>睡觉</li> </ol> {{end}}
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>干饭小白</title> </head> <body> <P> 我就喜欢干饭,哇哇哇哇 </P> </body> </html>
14.block(用于买吧继承)
定义一个根模板base.tmpl
package main import ( "fmt" "html/template" "net/http" ) func blockHandle(w http.ResponseWriter, r *http.Request) { //定义模板 //解析模板 tem, err := template.ParseGlob(".//*.tmpl") if err != nil { fmt.Println("template.ParseFiles err=", err) return } //执行模板 tem.ExecuteTemplate(w, "login.tmpl", "宠妻狂魔") } func main() { http.HandleFunc("/block", blockHandle) http.ListenAndServe(":9090", nil) }
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>干饭小白</title> <style> #dd{ color: aqua; } </style> </head> <body> <h2>模板继承 父</h2> <div class="dd"> {{block "con1" .}}{{end}} </div> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>干饭小白</title> </head> <body> <h2>模板继承 子</h2> <hr> <div> {{template "base.tmpl"}} <div> {{define "con1"}} HELLO {{end}} </div> </div> </body> </html>
15.修改默认的标识符{{ }}
在实际的项目开发中,有时候会用到其它的一些框架,这时候可能会与{{}}冲突。
GO提供了让我们直接定义标识符。《在解析模板之前可以定义》
template.New("test").Delims("{[", "]}").ParseFiles("./t.tmpl")
解析模板:
方法1:直接使用默认的模板template
func (t *Template) Parse(src string) (*Template, error) func ParseFiles(filenames ...string) (*Template, error) func ParseGlob(pattern string) (*Template, error)
方法2:创建自己的模板
func New(name string) *Template
渲染模板:
渲染模板就是用数据去填充模板的内容
func (t *Template) Execute(wr io.Writer, data interface{}) error func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error