模板引擎是 Web 编程中必不可少的⼀个组件。模板能分离逻辑和数据,使得逻辑简洁清晰,并且模板可复⽤。
⽆逻辑模板引擎
类模板引擎只进⾏字符串的替换,⽆其它逻辑。
嵌⼊逻辑模板引擎
此类模板引擎可以在模板中嵌⼊逻辑,实现流程控制/循环等。
package main
import (
"log"
"net/http"
"os"
"text/template"
)
type User struct {
Name string
Age int
}
// 解析字符串
func stringLiteralTemplate() {
s := "My name is {{ .Name }}. I am {{ .Age }} years old.\n"
// 创建模板 并解析上面的s字符串
t, err := template.New("test").Parse(s)
if err != nil {
log.Fatal("Parse string literal template error:", err)
}
u := User{Name: "lianshi", Age: 18}
// 去执行渲染 把数据按规则塞进模板
err = t.Execute(os.Stdout, u)
if err != nil {
log.Fatal("Execute string literal template error:", err)
}
}
// 解析文件 注意用终端执行
func fileTemplate() {
//t, err := template.ParseFiles("test")
t, err := template.ParseFiles("index.html")
if err != nil {
log.Fatal("Parse file template error:", err)
}
u := User{Name: "ls", Age: 18}
err = t.Execute(os.Stdout, u)
if err != nil {
log.Fatal("Execute file template error:", err)
}
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
// 前后端不分离 后端利用模板渲染
t, err := template.ParseFiles("index.html")
if err != nil {
log.Fatal("Parse file template error:", err)
}
u := User{Name: "ls", Age: 18}
err = t.Execute(w, u)
if err != nil {
log.Fatal("Execute file template error:", err)
}
// 前后端分离,返回Json格式的数据
// 优点:减少网络消耗和耗时
// 缺点:页面都是游览器渲染,不利于数据处理,排序等
//u := User{Name: "ls", Age: 18}
}
// 点动作
func main() {
//stringLiteralTemplate()
//fileTemplate()
mux := http.NewServeMux()
mux.HandleFunc("/index", indexHandler)
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
if err := server.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
package main
import (
"log"
"math/rand"
"os"
"text/template"
"time"
)
type AgeInfo struct {
Age int
GreaterThan60 bool
GreaterThan40 bool
}
// 条件动作
func main() {
t, err := template.ParseFiles("test")
if err != nil {
log.Fatal("Parse error:", err)
}
rand.Seed(time.Now().Unix())
age := rand.Intn(100)
info := AgeInfo{
Age: age,
GreaterThan60: age > 60,
GreaterThan40: age > 40,
}
err = t.Execute(os.Stdout, info)
if err != nil {
log.Fatal("Execute error:", err)
}
}
package main
import (
"log"
"os"
"text/template"
)
type Item struct {
Name string
Price int
}
// 迭代动作
func main() {
t, err := template.ParseFiles("test")
if err != nil {
log.Fatal("Parse error:", err)
}
items := []Item{
{"iPhone", 699},
{"iPad", 799},
{"iWatch", 199},
{"MacBook", 999},
}
err = t.Execute(os.Stdout, items)
if err != nil {
log.Fatal("Execute error:", err)
}
}
package main
import (
"log"
"os"
"text/template"
)
type User struct {
Name string
Age int
}
type Pet struct {
Name string
Age int
Owner User
}
// 传入的数据中,嵌套另外一个结构,则模板中 可以使用 with 关键字 来简化语法
func main() {
t, err := template.ParseFiles("test")
if err != nil {
log.Fatal("Parse error:", err)
}
p := Pet{
Name: "Orange",
Age: 2,
Owner: User{
Name: "ls",
Age: 18,
},
}
err = t.Execute(os.Stdout, p)
if err != nil {
log.Fatal("Execute error:", err)
}
}
test
Pet Info:
Name: {{ .Name }}
Age: {{ .Age }}
Owner:{{ with .Owner }}
Name: {{ .Name }}
Age: {{ .Age }}
{{ end }}
package main
import (
"log"
"os"
"text/template"
)
// 模板之间的嵌套——包含动作
func main() {
t, err := template.ParseFiles("test1", "test2")
if err != nil {
log.Fatal("Parse error:", err)
}
err = t.Execute(os.Stdout, "test data")
if err != nil {
log.Fatal("Execute error:", err)
}
}
test1
This is in test1.
{{ template "test2" }}
{{ template "test2" . }}
test2
This is in test2.
Get: {{ . }}.
package main
import (
"log"
"os"
"text/template"
)
type User struct {
FirstName string
LastName string
}
func (u User) FullName() string {
return u.FirstName + " " + u.LastName
}
func main() {
t, err := template.ParseFiles("test")
if err != nil {
log.Fatal("Parse error:", err)
}
err = t.Execute(os.Stdout, User{FirstName: "ls", LastName: "lianshi"})
if err != nil {
log.Fatal("Execute error:", err)
}
}
My full name is {{ .FullName }}.
输出:
My full name is ls lianshi.
在⼀个链式管道中,每个命令的结果会作为下⼀个命令的最后⼀个参数。最后⼀个命令的结果作为整个管道的值
package main
import (
"log"
"os"
"text/template"
)
type Item struct {
Name string
Price float64
Num int
}
func (item Item) Total() float64 {
return item.Price * float64(item.Num)
}
func main() {
t, err := template.ParseFiles("test")
if err != nil {
log.Fatal("Parse error:", err)
}
item := Item{"iPhone", 699.99, 2}
err = t.Execute(os.Stdout, item)
if err != nil {
log.Fatal("Execute error:", err)
}
}
test
Product: {{ .Name }}
Price: ¥{{ .Price }}
Num: {{ .Num }}
Total: ¥{{ .Total | printf "%.2f" }}
输出:
Product: iPhone
Price: ¥699.99
Num: 2
Total: ¥1399.98
$variable := pipeline
Go 模板提供了⼤量的预定义函数,如果有特殊需求也可以实现⾃定义函数。模板执⾏时,遇到函数调⽤,先从模板⾃定义函数表中查找,⽽后查找全局函数表。
package main
import (
"log"
"os"
"text/template"
"time"
)
func formatDate(t time.Time) string {
return t.Format("2016-01-02")
}
func main() {
funcMap := template.FuncMap{
"fdate": formatDate,
}
t := template.New("test").Funcs(funcMap)
t, err := t.ParseFiles("test")
if err != nil {
log.Fatal("Parse errr:", err)
}
err = t.Execute(os.Stdout, time.Now())
if err != nil {
log.Fatal("Exeute error:", err)
}
}
test
Today is {{ . | fdate }}.
输出:
Today is 12116-11-12.
第一种:
package main
import (
"log"
"os"
"text/template"
)
func main() {
t := template.New("test")
t, err := t.ParseFiles("test1")
if err != nil {
log.Fatal("Parse error:", err)
}
err = t.Execute(os.Stdout, nil)
if err != nil {
log.Fatal("Execute error:", err)
} }
出错:名字不一样
执⾏ ParseFiles ⽅法时,每个⽂件都会⽣成⼀个模板。只有⽂件基础名与模板名相同时,该⽂件的内容才会解析到主模板中。这也是上⾯的程序执⾏失败的原因——主模板为空。
应该作为参数传进去:
package main
import (
"log"
"os"
"text/template"
)
func main() {
t := template.New("test")
t, err := t.ParseFiles("test1")
if err != nil {
log.Fatal("in associatedTemplate Parse error:", err)
}
err = t.ExecuteTemplate(os.Stdout, "test1", nil)
if err != nil {
log.Fatal("in associatedTemplate Execute error:", err)
}
}
第二种:将创建和解析两步合并在⼀起了。
t, err := template.ParseFiles("file1", "file2", "file3")
等价于
t := template.New("file1")
t, err := t.ParseFiles("file1", "file2", "file3")
在⼀个模板⽂件中还可以通过 {{ define }} 动作定义其它的模板,这些模板就是嵌套模板。模板定义必须在模板内容的最顶层,像 Go 程序中的全局变量⼀样。
嵌套模板⼀般⽤于布局(layout)。很多⽂本的结构其实⾮常固定,例如邮件有标题和正⽂,⽹⻚有⾸部、正⽂和尾部等。 我们可以为这些固定结构的每部分定义⼀个模板。
{{ define "layout" }}
This is body.
{{ template "content" . }}
{{ end }}
{{ define "content" }}
This is {{ . }} content.
{{ end }}
上⾯定义了两个模板 layout 和 content , layout 中使⽤了 content 。执⾏这种⽅式定义的模板必须使⽤ ExecuteTemplate ⽅法:
func main() {
t, err := template.ParseFiles("layout.tmpl")
if err != nil {
log.Fatal("Parse error:", err)
}
err = t.ExecuteTemplate(os.Stdout, "layout", "amazing")
if err != nil {
log.Fatal("Execute error:", err)
}
}
块动作其实就是定义⼀个默认模板,语法如下:
{{ block "name" arg }}
T1
{{ end }}
它就等价于定义⼀个模板,然后⽴即使⽤它