教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang模板(text/template)

golang模板(text/template)

发布时间:2022-03-11   编辑:jiaochengji.com
教程集为您提供golang模板(text/template)等资源,欢迎您收藏本站,我们将为您提供最新的golang模板(text/template)资源
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"/></svg>

text/template是Go语言标准库,实现数据驱动模板以生成文本输出,可以理解为一组文字按照特定格式动态嵌入另一组文字中。

还有个处理html文字的模板(html/template),感兴趣的可以了解下。

<h3>简单字符</h3>

示例

<pre><code>package main import ( "os" "text/template" ) func CheckErr(err error) { if err != nil { panic(err) } } func main() { name := "world" tmpl, err := template.New("test").Parse("hello, {{.}}") //建立一个模板,内容是"hello, {{.}}" CheckErr(err) err = tmpl.Execute(os.Stdout, name) //将string与模板合成,变量name的内容会替换掉{{.}} //合成结果放到os.Stdout里 输出 CheckErr(err) } </code></pre>

输出: hello, world

模板的输入文本是任何格式的UTF-8编码文本。 {{ 和 }} 包裹的内容统称为 action,分为两种类型:

<ul><li>数据求值(data evaluations)</li><li>控制结构(control structures)</li></ul>

action 求值的结果会直接复制到模板中,控制结构和我们写 Go 程序差不多,也是条件语句、循环语句、变量、函数调用等等…

将模板成功解析(Parse)后,可以安全地在并发环境中使用,如果输出到同一个 <code>io.Writer</code> 数据可能会重叠(因为不能保证并发执行的先后顺序)。

这里{{和}}中间的句号(.)代表传入的数据,数据不同渲染不同,可以代表 go 语言中的任何类型,如结构体、哈希等。

<h3>
struct</h3>

示例

<pre><code>package main import ( "os" "text/template" ) func CheckErr(err error) { if err != nil { panic(err) } } func main() { type Inventory struct { Material string Count uint } sweaters := Inventory{"wool", 17} tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}") CheckErr(err) err = tmpl.Execute(os.Stdout, sweaters) CheckErr(err) } </code></pre>

输出:17 items are made of wool

<h3>
模板文件</h3>

新建一个模板文件 demo.go.tpl

内容

<pre><code>{{.Count}} items are made of {{.Material}} </code></pre>

上面示例可改为

<pre><code>package main import ( "os" "text/template" ) func CheckErr(err error) { if err != nil { panic(err) } } func main() { type Inventory struct { Material string Count uint } sweaters := Inventory{"wool", 17} tmpl, err := template.ParseFiles("demo.go.tpl") CheckErr(err) err = tmpl.Execute(os.Stdout, sweaters) CheckErr(err) } </code></pre>

输出:17 items are made of wool

<h3>
多模板</h3>

示例

<pre><code>package main import ( "fmt" "os" "text/template" ) func CheckErr(err error) { if err != nil { panic(err) } } func main() { type Inventory struct { Material string Count uint } sweaters := Inventory{"wool", 17} tmpl, err := template.New("T2").Parse("{{.Count}} items are made of") tmpl, err = tmpl.New("test").Parse("{{.Count}} items are made of {{.Material}}") CheckErr(err) err = tmpl.ExecuteTemplate(os.Stdout, "T2", sweaters) //可以选取模板 CheckErr(err) fmt.Println("") fmt.Println(tmpl.Name()) tmpl = tmpl.Lookup("test") //切换模板,必须要有返回,否则不生效 fmt.Println(tmpl.Name()) } </code></pre>

输出:

17 items are made of
test
test

<h3>
输出到指定文件</h3>

示例

<pre><code>package main import ( "os" "text/template" ) func CheckErr(err error) { if err != nil { panic(err) } } func main() { type Inventory struct { Material string Count uint } sweaters := Inventory{"wool", 17} tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}") CheckErr(err) file, err := os.OpenFile("demo.txt", os.O_CREATE|os.O_WRONLY, 0755) CheckErr(err) err = tmpl.Execute(file, sweaters) CheckErr(err) } </code></pre>

打开文件 demo.txt,内容为: 17 items are made of wool

<h3>
循环操作</h3>

使用方法

<pre><code>{{range .Field}} {{.ChildFieldOne}} -- {{.ChildFieldTwo }} {{ end }} </code></pre>

示例

<pre><code>package main import ( "os" "text/template" ) func CheckErr(err error) { if err != nil { panic(err) } } func main() { type Inventory struct { Material string Count uint } type NewInventory struct { Fields []Inventory } sweaters := NewInventory{ Fields: []Inventory{ Inventory{Material: "wool", Count: 19}, Inventory{Material: "wooltwo", Count: 20}, }} var Text = ` {{range .Fields }} Material: {{.Material}} - Count:{{.Count}} {{ end }} ` tmpl, err := template.New("test").Parse(Text) CheckErr(err) err = tmpl.Execute(os.Stdout, sweaters) CheckErr(err) } </code></pre> <h3>
模板函数</h3>

即:可以对某个字段使用函数操作。适用于稍微复杂的字段处理。

<pre><code>type FuncMap map[string]interface{} t = t.Funcs(template.FuncMap{"handleFieldName": HandleFunc}) </code></pre>

内置模板函数:

<pre><code>var builtins = FuncMap{ "and": and, "call": call, "html": HTMLEscaper, "index": index, "js": JSEscaper, "len": length, "not": not, "or": or, "print": fmt.Sprint, "printf": fmt.Sprintf, "println": fmt.Sprintln, "urlquery": URLQueryEscaper, } </code></pre>

示例

<pre><code>package main import ( "os" "text/template" ) func CheckErr(err error) { if err != nil { panic(err) } } func main() { type Inventory struct { Material string Count uint } type NewInventory struct { Fields []Inventory } sweaters := NewInventory{ Fields: []Inventory{ Inventory{Material: "wool", Count: 19}, Inventory{Material: "wooltwo", Count: 20}, }} var Text = ` {{range .Fields }} Material: {{.Material | handleString}} - Count:{{.Count | handleInt }} {{ end }} ` tmpl, err := template.New("test").Funcs(template.FuncMap{"handleString": handleString, "handleInt": handleInt}).Parse(Text) CheckErr(err) err = tmpl.Execute(os.Stdout, sweaters) CheckErr(err) } func handleInt(number uint) uint { return number 10 } func handleString(field string) string { return " string is: " field } </code></pre>

输出:

Material: string is: wool - Count:29

Material: string is: wooltwo - Count:30

<h2>
Actions</h2> <h3>注释</h3> <pre><code>{{*/\* comment \*/*}} </code></pre> <h3>裁剪空格</h3> <pre><code>// 裁剪 content 前后的空格 {{- content -}} // 裁剪 content 前面的空格 {{- content }} // 裁剪 content 后面的空格 {{ content -}} </code></pre> <h3>管道函数</h3> <pre><code>用法1: {{FuncName1}} 此标签将调用名称为“FuncName1”的模板函数(等同于执行“FuncName1()”,不传递任何参数)并输出其返回值。 用法2: {{FuncName1 "参数值1" "参数值2"}} 此标签将调用“FuncName1("参数值1", "参数值2")”,并输出其返回值 用法3: {{.Admpub|FuncName1}} 此标签将调用名称为“FuncName1”的模板函数(等同于执行“FuncName1(this.Admpub)”,将竖线“|”左边的“.Admpub”变量值作为函数参数传送)并输出其返回值。 </code></pre> <h3>文本输出</h3> <pre><code>{{ pipeline }} </code></pre>

pipeline 代表的数据会产生与调用 <code>fmt.Print</code> 函数类似的输出,例如整数类型的 3 会转换成字符串 “3” 输出。

<h3>
条件语句</h3> <pre><code>{{ if pipeline }} T1 {{ end }} {{ if pipeline }} T1 {{ else }} T0 {{ end }} {{ if pipeline }} T1 {{ else if pipeline }} T0 {{ end }} *// 上面的语法其实是下面的简写* {{ if pipeline }} T1 {{ else }}{{ if pipeline }} T0 { {end }}{{ end }} {{ if pipeline }} T1 {{ else if pipeline }} T2 {{ else }} T0 {{ end }} </code></pre> <h3>循环语句</h3> <pre><code>{{ range pipeline }} T1 {{ end }} *// 这个 else 比较有意思,如果 pipeline 的长度为 0 则输出 else 中的内容* {{ range pipeline }} T1 {{ else }} T0 {{ end }} *// 获取容器的下标* {{ range $index, $value := pipeline }} T1 {{ end }} </code></pre>

pipeline 的值必须是数组、切片、字典和通道中的一种,即可迭代类型的值,根据值的长度输出多个 T1。

<h3>
define</h3> <pre><code>{{ define "name" }} T {{ end }} </code></pre>

定义命名为 name 的模板。

<h3>
template</h3> <pre><code>{{ template "name" }} {{ template "name" pipeline }} </code></pre>

引用命名为 name 的模板。

<h3>
block</h3> <pre><code>{{ block "name" pipeline }} T1 {{ end }} </code></pre>

block 的语义是如果有命名为 name 的模板,就引用过来执行,如果没有命名为 name 的模板,就是执行自己定义的内容。

也就是多做了一步模板是否存在的判断,根据这个结果渲染不同的内容。

<h3>
with</h3> <pre><code>{{ with pipeline }} T1 {{ end }} // 如果 pipeline 是空值则输出 T0 {{ with pipeline }} T1 {{ else }} T0 {{ end }} {{ with arg }} . // 此时 . 就是 arg {{ end }} </code></pre>

with 创建一个新的上下文环境,在此环境中的 <code>.</code> 与外面的 <code>.</code> 无关。

<h2>
参数</h2>

参数的值有多种表现形式,可以求值任何类型,包括函数、指针(指针会自动间接取值到原始的值):

<ul><li>

布尔、字符串、字符、浮点数、复数的行为和 Go 类似

</li><li>

关键字 <code>nil</code> 代表 go 语言中的 <code>nil</code>

</li><li>

字符句号 . 代表值的结果

</li><li>

以 $ 字符开头的变量则为变量对应的值

</li><li>

结构体的字段表示为 <code>.Field</code>,结果是 Field 的值,支持链式调用 <code>.Field1.Field2</code>

</li><li>

字典的 key 表示为 <code>.Key</code> 结果是 Key 对应的值

</li><li>

如果是结构体的方法集中的方法 .Method 结果是方法调用后返回的值(The result is the value of invoking the method with dot as the receiver)**

<ul><li>方法要么只有一个任意类型的返回值要么第二个返回值为 error,不能再多了,如果 error 不为 nil,会直接报错,停止模板渲染</li><li>方法调用的结果可以继续链式调用 <code>.Field1.Key1.Method1.Field2.Key2.Method2</code></li><li>声明变量方法集也可以调用 <code>$x.Method1.Field</code></li><li>用括号将调用分组 <code>print (.Func1 arg1) (.Func2 arg2)</code> 或 <code>(.StructValuedMethod "arg").Field</code></li></ul></li></ul><h2>
变量</h2>

action 中的 pipeline 可以初始化变量存储结果,语法也很简单:

<pre><code>$variable = pipeline </code></pre>

此时,这个 action 声明了一个变量而没有产生任何输出。

range 循环可以声明两个变量:

<pre><code>range $index, $element := pipeline </code></pre>

在 if、with 和 range 中,变量的作用域拓展到 {{ end }} 所在的位置。

如果不是控制结构,声明的变量的作用域会扩展到整个模板。

例如在模板开始时声明变量:

<pre><code>{{ $pages := .pagination.Pages }} {{ $current := .pagination.Current }} </code></pre>

在渲染开始的时候,<code>$</code> 变量会被替换成 <code>.</code> 开头的值,例如 <code>$pages</code> 会被替换成 <code>.pagenation.Pages</code>。所以在模板间的相互引用不会传递变量,变量只在某个特定的作用域中产生作用。

<h2>
函数</h2>

模板渲染时会在两个地方查找函数:

<ul><li>自定义的函数 map</li><li>全局函数 map,这些函数是模板内置的</li></ul>

自定义函数使用 <code>func (t *Template) Funcs(funcMap FuncMap) *Template</code> 注册。

全局函数列表:

<h3>
and</h3>

返回参数之间 and 布尔操作的结果,其实就是 JavaScript 中的逻辑操作符 <code>&&</code>,返回第一个能转换成 false 的值,在 Go 中就是零值,如果都为 true 返回最后一个值。

<pre><code>tpl := "{{ and .x .y .z }}" t, _ := template.New("test").Parse(tpl) t.Execute(os.Stdout, map[string]interface{}{ "x": 1, "y": 0, "z": 3, }) output: 0 </code></pre> <h3>
or</h3>

逻辑操作符 <code>||</code>,返回第一个能转换成 true 的值,在 Go 中就是非零值,如果都为 false 返回最后一个值。

<pre><code>tpl := "{{ or .x .y .z }}" t, _ := template.New("test").Parse(tpl) t.Execute(os.Stdout, map[string]interface{}{ "x": 1, "y": 0, "z": 3, }) output: 1 </code></pre> <h3>
call</h3>

返回调用第一个函数参数的结果,函数必须有一个或两个回值(第二个返回值必须是 error,如果值不为 nil 会停止模板渲染)

<pre><code>tpl := "call: {{ call .x .y .z }} \n" t, _ := template.New("test").Parse(tpl) t.Execute(os.Stdout, map[string]interface{}{ "x": func(x, y int) int { return x y}, "y": 2, "z": 3, }) output: 5 </code></pre> <h3>
html</h3>

返回转义后的 HTML 字符串,这个函数不能在 <code>html/template</code> 中使用。

<h3>
js</h3>

返回转义后的 JavaScript 字符串。

<h3>
index</h3>

在第一个参数是 array、slice、map 时使用,返回对应下标的值。

<code>index x 1 2 3</code> 等于 <code>x[1][2][3]</code>。

<h3>
len</h3>

返回复合类型的长度。

<h3>
not</h3>

返回布尔类型参数的相反值。

<h3>
print</h3>

等于 <code>fmt.Sprint</code>。

<h3>
printf</h3>

等于 <code>fmt.Sprintf</code>。

<h3>
println</h3>

等于 <code>fmt.Sprintln</code>。

<h3>
urlquery</h3>

对字符串进行 url Query 转义,不能在 <code>html/template</code> 包中使用。

<pre><code>// URLQueryEscaper returns the escaped value of the textual representation of // its arguments in a form suitable for embedding in a URL query. func URLQueryEscaper(args ...interface{}) string { return url.QueryEscape(evalArgs(args)) } </code></pre>

从源码可以看到这个函数直接调用 <code>url.QueryEscape</code> 对字符串进行转义,并没有什么神秘的。

<h3>
比较函数</h3> <ul><li><code>eq</code>: ==</li><li><code>ge</code>: >=</li><li><code>gt</code>: ></li><li><code>le</code>: <=</li><li><code>lt</code>: <</li><li><code>ne</code>: !=</li></ul>

分析两个源码:

<pre><code>// eq evaluates the comparison a == b || a == c || ... func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) { v1 := indirectInterface(arg1) k1, err := basicKind(v1) if err != nil { return false, err } if len(arg2) == 0 { return false, errNoComparison } for _, arg := range arg2 { v2 := indirectInterface(arg) k2, err := basicKind(v2) if err != nil { return false, err } truth := false if k1 != k2 { // Special case: Can compare integer values regardless of type's sign. switch { case k1 == intKind && k2 == uintKind: truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint() case k1 == uintKind && k2 == intKind: truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int()) default: return false, errBadComparison } } else { switch k1 { case boolKind: truth = v1.Bool() == v2.Bool() case complexKind: truth = v1.Complex() == v2.Complex() case floatKind: truth = v1.Float() == v2.Float() case intKind: truth = v1.Int() == v2.Int() case stringKind: truth = v1.String() == v2.String() case uintKind: truth = v1.Uint() == v2.Uint() default: panic("invalid kind") } } if truth { return true, nil } } return false, nil } // ne evaluates the comparison a != b. func ne(arg1, arg2 reflect.Value) (bool, error) { // != is the inverse of ==. equal, err := eq(arg1, arg2) return !equal, err } </code></pre>

eq 先判断接口类型是否相等,然后判断值是否相等,没什么特殊的地方。

ne 更是简单的调用 eq,然后取反。

ge、gt、le、lt 与 eq 类似,先判断类型,然后判断大小。

<h2>
嵌套模板</h2>

下面是一个更复杂的例子:

<pre><code>// 加载模板 template.ParseFiles("templates/") // 加载多个模板到一个命名空间(同一个命名空间的模块可以互相引用) template.ParseFiles("header.tmpl", "content.tmpl", "footer.tmpl") // must 加载失败时 panic tmpl := template.Must(template.ParseFiles("layout.html")) // 执行加载后的模板文件,默认执行第一个 tmpl.Execute(w, "test") // 如果 tmpl 中有很多个模板,可以指定要执行的模板名 tmpl.ExecuteTemplate(w, "layout", "Hello world") </code></pre>

<code>ExecuteTemplate</code> 指定的名字就是模板文件中 <code>define "name"</code> 的 name。

参考:

https://golang.org/pkg/text/template/

https://juejin.im/post/5c403b98f265da612d1984c9

<h2>
links</h2> <ul><li>目录</li></ul> 到此这篇关于“golang模板(text/template)”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
Golang 模板(text/template) (一)
golang template实现模板layout及传递funcMaps
golang 网络编程(3)
jquery.mustache.js使用
PHPCMS v9 如何在手机端栏目绑定模板
golang 模板(template)的常用基本语法
php生成静态页面程序
php模板引擎有哪些
golang模板(text/template)
Json2Template.js 基于jquery的插件 绑定JavaScript对象到Html模板中

[关闭]
~ ~