模板引擎

寒江蓑笠翁大约 8 分钟

模板引擎

html/template包提供了数据驱动的模板,用于生成可以对抗代码注入的HTML安全输出。一般在传统的MVC架构中,前后端不分离的情况下,为了实现动态网页的效果,模板引擎是必不可少的。Java中著名的有freemaker themyleaf,在go语言中,标准库的模板包就足够基本使用了。

作用域

先看一个示例

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>Go Web</title>
    </head>
    <body>
        {{ . }}
    </body>
</html>

.表示的是当前作用域对象,如果传入的是字符串,那么就是字符串,如果是对象,还可以通过.field来访问。

解析

func main() {
   tempFile, err := template.ParseFiles("test.html")
   if err != nil {
      fmt.Println(err)
      return
   }
   tempFile.Execute(os.Stdout,"hello")
}

去除空白

template在解析时,会保留每一个字符,倘若想要去除一些空白字符时,可以使用-来进行对应的操作。

例如

{{30 -}} > {{15}}

会去除掉}} >之间的空格,最终效果是30> 15,下方的例子同理

{{30 -}} > {{- 15}}

得到30>15

管道

其类似于unix系统的管道运算符,是产生数据的操作,即pipeline,并不是只有|才是pipeline,只要能产生数据的操作都是pipeline

{{.}} | printf "%s"

再例如,括号中的操作也是pipeline

{{println (len "hello world")}}

变量

模板中可以定义变量,语法如下

$val := pipeline

例子

{{$length := (len "hello")}}
{{println $length }}

需要注意的地方:

  • 变量的作用域,只要出现end,当前层次的作用域结束,内层变量可以访问外层,但是外层不能访问内层。
  • 全局变量$是模板的最顶级作用域对象,且一直不会有变化。
  • 变量不可以在模板之间继承

条件判断

语法如下,理解起来比较简单

{{if pipeline}} condition1 {{end}}
{{if pipeline}} condition1 {{else}} condition2 {{end}}
{{if pipeline}} condition1 {{else if pipeline2}} condition2 {{else}} condition3 {{end}} 

迭代

可以适用于切片,map,数组,或者channel

语法

{{range pipeline}} 1 {{end}} //为0值时直接跳过
{{range pipeline}} 1 {{else}} 2 {{end}} //为0值时执行else

也可以迭代赋值,就像写go循环一样

{{range $val := pipeline}}
{{range $key,$val := pipeline}}

With

with end用于设置当前作用域对象的值,即.

{{with pipeline}} 1 {{end}} //为0值是跳过
{{with pipeline}} 1 {{else}} 2 {{end}} //为0值时执行else

函数

以下是内置函数

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逸码等价表示。
    
urlquery
    返回其参数文本表示的可嵌入URL查询的逸码等价表示。
    
js
    返回其参数文本表示的JavaScript逸码等价表示。
    
call
    执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;
    如"call .X.Y 1 2"等价于go语言里的dot.X.Y(1, 2);
    其中Y是函数类型的字段或者字典的值,或者其他类似情况;
    call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);
    该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型;
    如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误; 
    
eq      如果arg1 == arg2则返回真

ne      如果arg1 != arg2则返回真

lt      如果arg1 < arg2则返回真

le      如果arg1 <= arg2则返回真

gt      如果arg1 > arg2则返回真

ge      如果arg1 >= arg2则返回真 

也可以自定义函数在模板中使用

myFunc := func(arg string) (string, error) {
        return "hello", nil
}
//在Parse之前调用Funcs添加自定义的myFunc函数
tmpl, err := template.New("hello").Funcs(template.FuncMap{"myFunc": myFunc}).Parse(string(htmlByte))
{{myFunc "world"}}

嵌套Template

可以在Template中嵌套Template,可以是文件,也可以是define定义的Template

test.txt

This is a test

main.txt

{{template test.txt}}
{{template virtual.txt}}
This is main
{{define "virtual.txt"}}
This a define template

解析main.txt文件效果如下

This is a test
This a define template
This is main