GO-模板引擎

第 6 章:模板引擎

Go 为我们提供了 text/template 库和 html/template 库这两个模板引擎,模板引擎通过将数据和模板组合在一起生成最终的 HTML,而处理器负责调用模板引擎并将引擎生成的 HTMl 返回给客户端。

Go 的模板都是文本文档(其中 Web 应用的模板通常都是 HTML),它们都嵌入了一些称为动作的指令。从模板引擎的角度来说,模板就是嵌入了动作的文本(这些文本通常包含在模板文件里面),而模板引擎则通过分析并执行这些文本来生成出另外一些文本。

6.1 HelloWorld

使用 Go 的 Web 模板引擎需要以下两个步骤:
(1) 对文本格式的模板源进行语法分析,创建一个经过语法分析的模板结构,其中模板源既可以是一个字符串,也可以是模板文件中包含的内容。

(2 )执行经过语法分析的模板,将 ResponseWriter 和模板所需的动态数据传递给模板引擎,被调用的模板引擎会把经过语法分析的模板和传入的数据结合起来,生成出最终的 HTML,并将这些 HTML 传递给 ResponseWriter。下面就让我们写一个简单的 HelloWorld

  1. 创建模板文件 hello.html
<html>
	<head>
		<title>
			模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		//嵌入动作 {{.}}
	</body>

</html>
  1. 在处理器中触发模板引擎
func handler(w http.ResponseWriter, r * http.Request) {
	//解析模板文件
	t,
	_: = template.ParseFiles("hello.html")
	//执行模板
	t.Execute(w, "Hello World!")
}
  1. 浏览器中的结果
Hello World!

6.2 解析模板

  1. ParseFiles 函数
    在这里插入图片描述
  • 当我们调用 ParseFiles 函数解析模板文件时,Go 会创建一个新的模板,并将给定的模板文件的名字作为新模板的名字,如果该函数中传入了多个文件名,那么也只会返回一个模板,而且以第一个文件的文件名作为模板的名字,至于其他文件对应的模板则会被放到一个 map 中。让我们再来看一下 HelloWorld 中的代码:
t, _ := template.ParseFiles("hello.html")
  • 以上代码相当于调用 New 函数创建一个新模板,然后再调用 template 的ParseFiles 方法:
t := template.New("hello.html")
t, _ = t.ParseFiles("hello.html")

在这里插入图片描述
在这里插入图片描述

  • 我们在解析模板时都没有对错误进行处理,Go 提供了一个 Must 函数专门用来处理这个错误。Must 函数可以包裹起一个函数,被包裹的函数会返回一个指向模板的指针和一个错误,如果错误不是 nil,那么 Must 函数将产生一个 panic。
    在这里插入图片描述

  • 实验 Must 函数之后的代码

t := template.Must(template.ParseFiles("hello.html"))
  1. ParseGlob 函数
    在这里插入图片描述
  • 通过该函数可以通过指定一个规则一次性传入多个模板文件,如:
t, _ := template.ParseGlob("*.html")

6.3 执行模板

  1. 通过 Execute 方法
    在这里插入图片描述
  • 如果只有一个模板文件,调用这个方法总是可行的;但是如果有多个模板文件,调用这个方法只能得到第一个模板
  1. 通过 ExecuteTemplate 方法
    在这里插入图片描述
  • 例如:
t, _ := template.ParseFiles("hello.html", "hello2.html")
  • 变量 t 就是一个包含了两个模板的模板集合,第一个模板的名字是hello.html,第二个模板的名字是 hello2.html,如果直接调用 Execute 方法,则只有模板 hello.html 会被执行,如何想要执行模板 hello2.html,则需要调用 ExecuteTemplate 方法
t.ExecuteTemplate(w, "hello2.html", "我要在 hello2.html 中显示")

6.4 动作

Go 模板的动作就是一些嵌入到模板里面的命令,这些命令在模板中需要放到两个大括号里{{ 动作 }},之前我们已经用过一个很重要的动作:点(.),它代表了传递给模板的数据。下面我们再介绍几个常用的动作,如果还想了解其他类型的动作,可以参考text/template 库的文档。

6.4.1 条件动作

  • 格式一:
{{ if arg}}
要显示的内容
{{ end }}
  • 格式二:
{{ if arg}}
要显示的内容
{{else}}if 条件不满足时要显示的内容
{{ end }}
  • 其中的 arg 是传递给条件动作的参数,该值可以是一个字符串常量、一个变量、一个返回单个值的函数获取方法等。
  • 例如:
  • 模板文件
<html>
	<head>
		<title>
			模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		<!-- 嵌入动作 -->
		{{if .}} 你已经成年了! {{else}} 你还未成年 {{end}}
	</body>

</html>
  • 处理器端代码
func handler(w http.ResponseWriter, r * http.Request) {
	//解析模板文件
	t: =template.Must(template.ParseFiles("hello.html"))
	//声明一个变量
	age: =16
	//执行模板
	t.Execute(w, age > 18)
}
  • 浏览器中的结果
你还未成年

6.4.2 迭代动作

迭代动作可以对数组、切片、映射或者通道进行迭代。

  • 格式一:
{{range . }}
遍历到的元素是 {{ . }}
{{ end }}
  • 格式二:
{{range . }}
遍历到的元素是 {{ . }}
{{ else }}
没有任何元素
{{ end }}
  • range 后面的点代表被遍历的元素;要显示的内容里面的点代表遍历到的元素
  • 例如:
  • 模板文件
<html>
	<head>
		<title>
			模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		<!-- 嵌入动作 -->
		{{range .}}
		<a href="#">
			{{.}}
		</a>
		{{else}} 没有遍历到任何内容 {{end}}
	</body>

</html>
  • 处理器端代码
func handler(w http.ResponseWriter, r * http.Request) {
	//解析模板文件
	t: =template.Must(template.ParseFiles("hello.html"))
	//声明一个字符串切片
	stars: =[] string {
		"马蓉",
		"李小璐",
		"白百何"
	}
	//执行模板
	t.Execute(w, stars)
}
  • 浏览器中的结果
马蓉 李小璐 白百何
  • 如果迭代之后是一个个的结构体,获取结构体中的字段值使用 .字段名方式获取
{{range . }}
获取结构体的 Name 字段名 {{ .Name }}
{{ end }}
  • 迭代 Map 时可以设置变量,变量以$开头:
{{ range $k , $v := . }}
键是 {{ $k }} , 值是 {{ $v }}
{{ end }}
  • 迭代管道
{{ c1 | c2 | c3 }}
  • c1、c2 和 c3 可以是参数或者函数。管道允许用户将一个参数的输出传递给下一个参数,各个参数之间使用 | 分割。

6.4.3 设置动作

设置动作允许在指定的范围内对点(.)设置值。

  • 格式一:
{{ with arg }}
为传过来的数据设置的新值是{{ . }}
{{ end }}
  • 格式二:
{{ with arg }}
为传过来的数据设置的新值是{{ . }}
{{ else }}
传过来的数据仍然是{{ . }}
{{ end }}
  • 例如:
  • 模板文件
<html>
	<head>
		<title>
			模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		<!-- 嵌入动作 -->
		<div>
			得到的数据是:{{.}}
		</div>
		{{with "太子"}}
		<div>
			替换之后的数据是:{{.}}
		</div>
		{{end}}
		<hr/>
		{{with ""}}
		<div>
			看一下现在的数据是:{{.}}
		</div>
		{{else}}
		<div>
			数据没有被替换,还是:{{.}}
		</div>
		{{end}}
	</body>

</html>
  • 处理器端代码
func handler(w http.ResponseWriter, r * http.Request) {
	//解析模板文件
	t: = template.Must(template.ParseFiles("hello.html"))
	//执行模板
	t.Execute(w, "狸猫")
}
  • 浏览器中的结果
得到的数据是:狸猫
替换之后的数据是:太子
数据没有被替换,还是:狸猫

6.4.4 包含动作

包含动作允许用户在一个模板里面包含另一个模板,从而构建出嵌套的模板。

  • 格式一:{{ template “name” }}
    • name 为被包含的模板的名字
  • 格式二:{{ template “name” arg }}
    • arg 是用户想要传递给被嵌套模板的数据
  • 例如:
    • 模板文件
    • hello.html
<html>
	<head>
		<title>
			模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		<!-- 嵌入动作 -->
		<div>
			从后台得到的数据是:{{.}}
		</div>
		<!-- 包含 hello2.html 模板 -->
		{{ template "hello2.html"}}
		<div>
			hello.html 文件内容结束
		</div>
		<hr/>
		<div>
			将 hello.html 模板文件中的数据传递给 hello2.html 模板文件
		</div>
		{{ template "hello2.html" . }}
	</body>

</html>
  • hello2.html
<html>
	<head>
		<title>
			hello2 模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		<!-- 嵌入动作 -->
		<div>
			hello2.html 模板文件中的数据是:{{.}}
		</div>
	</body>

</html>
  • 处理器端代码
func handler(w http.ResponseWriter, r * http.Request) {
	//解析模板文件
	t: = template.Must(template.ParseFiles("hello.html", "hello2.html"))
	//执行模板
	t.Execute(w, "测试包含")
}

注意:在解析模板文件时,当前文件以及被包含的文件都要解析

  • 浏览器中的结果
从后台得到的数据是:测试包含
hello2.html 模板文件中的数据是:
hello.html 文件内容结束
将 hello.html 模板文件中的数据传递给 hello2.html 模板文件
hello2.html 模板文件中的数据是:测试包含

6.4.5 定义动作

当我们访问一些网站时,经常会看到好多网页中有相同的部分:比如导航栏、版权信息、联系方式等。这些相同的布局我们可以通过定义动作在模板文件中定义模板来实现。定义模板的格式是:以{{ define “layout” }}开头,以{{ end }}结尾。

  1. 在一个模板文件(hello.html)中定义一个模板
<!-- 定义模板 -->
{{ define "model"}}
<html>
	<head>
		<title>
			模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		{{ template "content"}}
	</body>

</html>
{{ end }}
  1. 在一个模板文件中定义多个模板
  • 模板文件(hello.html)
<!-- 定义模板 -->
{{ define "model"}}
<html>
	<head>
		<title>
			模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		{{ template "content"}}
	</body>

</html>
{{ end }} {{ define "content"}}
<a href="#">
	点我有惊喜
</a>
{{ end }}
  • 处理器端代码
func handler(w http.ResponseWriter, r * http.Request) {
	//解析模板文件
	t: = template.Must(template.ParseFiles("hello.html"))
	//执行模板
	t.ExecuteTemplate(w, "model", "")
}
  • 注意:需要调用 ExecuteTemplate 方法并指定模板的名字
  • 浏览器中的结果
点我有惊喜
  1. 在不同的模板文件中定义同名的模板
  • 模板文件
  • hello.html
<!-- 定义模板 -->
{{ define "model"}}
<html>
	<head>
		<title>
			模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		{{ template "content"}}
	</body>

</html>
{{ end }}
  • content1.html
<html>
	<head>
		<title>
			content 模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		<!-- 定义 content 模板 -->
		{{ define “content” }}
		<h1>
			我是 content1.html 模板文件中的内容
		</h1>
		{{ end }}
	</body>

</html>
  • content2.html
<html>
	<head>
		<title>
			content 模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		<!-- 定义 content 模板 -->
		{{ define “content” }}
		<h1>
			我是 content2.html 模板文件中的内容
		</h1>
		{{ end }}
	</body>

</html>
  • 处理器端代码
func handler(w http.ResponseWriter, r * http.Request) {
	rand.Seed(time.Now().Unix())
	var t * template.Template
	if rand.Intn(5) > 2 {
		//解析模板文件
		t = template.Must(template.ParseFiles("hello.html",
			"content1.html"))
	} else {
		//解析模板文件
		t = template.Must(template.ParseFiles("hello.html",
			"content2.html"))
	}
	//执行模板
	t.ExecuteTemplate(w, "model", "")
}
  • 浏览器中的结果
我是 content1.html 模板文件中的内容

6.4.6 块动作

Go 1.6 引入了一个新的块动作,这个动作允许用户定义一个模板并立即使用。相当于设置了一个默认的模板

  • 格式:
{{ block arg }}
如果找不到模板我就要显示了
{{ end }}
  • 修改 6.4.5 中的模板文件 hello.html
<!-- 定义模板 -->
{{ define "model"}}
<html>
	<head>
		<title>
			模板文件
		</title>
		<meta charset="utf-8" />
	</head>
	<body>
		{{ block "content" .}} 如果找不到就显示我 {{ end }}
	</body>

</html>
{{ end }}
  • 稍微修改一下处理器端的代码
func handler(w http.ResponseWriter, r * http.Request) {
	rand.Seed(time.Now().Unix())
	var t * template.Template
	if rand.Intn(5) > 2 {
		//解析模板文件
		t = template.Must(template.ParseFiles("hello.html",
			"content1.html"))
	} else {
		//解析模板文件
		t = template.Must(template.ParseFiles("hello.html"))
	}
	//执行模板
	t.ExecuteTemplate(w, "model", "")
}
  • 浏览器中的结果
如果找不到就显示我
发布了2086 篇原创文章 · 获赞 2302 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/weixin_42528266/article/details/105348980