news 2026/8/3 16:23:44

Go 模板语法详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Go 模板语法详解

🥰个人主页:会编程的土豆(欢迎来访)
💎作者简介:后端学习者
❄️个人专栏:数据结构与算法,数据库,leetcode
那些你一个人走过的夜路,终将化作照亮未来的光

目标:吃透{{ }}里的动作(action):输出、变量、条件、循环、with、管道与内置函数。这是后面嵌套/继承/Gin 的语法基础。


1. 模板语法总览

Go 模板里,普通文本原样输出;逻辑都写在action中:

{{ ... }}

常见类别:

类别例子作用
输出{{.Name}}打印值
注释{{/* ... */}}不输出
变量{{$x := .}}绑定到$x
条件{{if}}...{{end}}分支
循环{{range}}...{{end}}遍历
改上下文{{with}}...{{end}}临时切换.
管道{{. | printf "%s"}}函数链式处理
模板调用{{template "x" .}}嵌套(下节细讲)

2. 点(dot).:当前上下文

.是模板里最重要的概念

  • Execute(w, data)时,根上下文.=data
  • 进入range/with后,.可能变成「当前元素」
  • 离开range/with后,.恢复

例子

type Product struct { Name string Price int } type Page struct { ShopName string Items []Product }
店铺:{{.ShopName}} {{range .Items}} <!-- 这里的 . 是 Product --> 商品:{{.Name}},价格:{{.Price}} {{end}} <!-- 这里 . 又回到 Page --> 再次显示店铺:{{.ShopName}}

如果在range里还想访问外层的ShopName,不能写{{.ShopName}}(因为.已是 Product),要用变量把外层存起来


3. 输出与注释

3.1 输出字段 / 键 / 方法

{{.Name}} <!-- struct 导出字段 --> {{.User.Email}} <!-- 链式字段 --> {{index .Map "k"}} <!-- map 取值的稳妥写法 --> {{.FullName}} <!-- 无参方法,方法名需导出 -->

方法示例:

type User struct { First string Last string } func (u User) FullName() string { return u.First + " " + u.Last }
{{.FullName}}

注意:方法若返回(value, error),模板执行时若 error != nil 会失败

3.2 注释

{{/* 这是注释,不会出现在最终 HTML 里 */}}

注释不能嵌套,内容里也不要随便写*/}}这类结束符

3.3 什么时候会「空输出」?

访问不到的字段、nil 指针的字段等,在默认设置下可能输出为空,或在missingkey等选项下报错。调试时建议先把数据结构打印出来,确认字段名大小写一致


4. 变量:$name

$定义模板变量:

{{$title := .Title}} <h1>{{$title}}</h1>

为什么需要变量?

最经典场景:range 里访问外层数据

{{$shop := .ShopName}} {{range .Items}} <li>{{$shop}} - {{.Name}}</li> {{end}}

也可以在range时直接接收索引和元素:

{{range $i, $item := .Items}} <li>#{{$i}} {{$item.Name}}</li> {{end}}

赋值与重新绑定

{{$x := 1}} {{$x = 2}} <!-- 重新赋值(Go 1.11+ 模板支持) -->

入门更常见的是:=定义一次后只读使用

作用域

变量一般在定义它的{{if}}/{{range}}/{{with}}/...{{end}}块内有效;在块外可能不可见(以你使用的 Go 版本与定义位置为准)。实践建议:在需要的最小范围内定义


5. 条件:if/else/else if

{{if .LoggedIn}} <p>欢迎,{{.Name}}</p> {{else}} <p>请先登录</p> {{end}}

什么算「真」?

模板把下列值视为false(不进入if主体):

  • false
  • 数字0
  • 空字符串""
  • nil
  • 空的 array / slice / map / chan

其它一般视为 true。

多分支

{{if eq .Role "admin"}} 管理员面板 {{else if eq .Role "editor"}} 编辑面板 {{else}} 普通用户 {{end}}

比较函数(很常用)

这些是内置函数,不是操作符写成==

函数含义
eq==
ne!=
lt<
le<=
gt>
ge>=
{{if gt .Age 18}} 成年 {{end}} {{if eq .Status "ok" "ready"}} <!-- eq 支持多参数:与任一相等即为真 --> 可用 {{end}}

逻辑与或非:

{{if and .A .B}} ... {{end}} {{if or .A .B}} ... {{end}} {{if not .Hidden}} ... {{end}}

6.with:临时切换上下文

with有两个作用:

  1. 判断值是否为真
  2. 若为真,在块内把.换成这个值
{{with .Profile}} <p>城市:{{.City}}</p> <p>签名:{{.Bio}}</p> {{else}} <p>用户未填写资料</p> {{end}}

等价心智模型:

如果 Profile 有值: 进入块,且 . = Profile 否则: 走 else

with+ 变量

{{with $p := .Profile}} {{$p.City}} {{end}}

ifvswith

ifwith
是否切换.
适合纯条件判断深入嵌套结构体字段

很多人写{{if .Profile}}{{.Profile.City}}{{end}},用with更干净。


7.range:遍历

可遍历:slice、array、map、channel。

7.1 基础遍历

<ul> {{range .Items}} <li>{{.Name}} - ¥{{.Price}}</li> {{else}} <li>暂无商品</li> {{end}} </ul>

else分支在集合为空时执行,非常适合空状态 UI。

7.2 索引与元素

{{range $index, $product := .Items}} <tr> <td>{{$index}}</td> <td>{{$product.Name}}</td> </tr> {{end}}

遍历 map:

{{range $key, $value := .Labels}} <span>{{$key}}={{$value}}</span> {{end}}

注意:map 的遍历顺序不稳定(随机),若要稳定展示,应在 Go 里先排好序再传给模板,或传[]struct{Key, Value}

7.3 range 里修改了.

{{range .Items}} <!-- . 是元素 --> {{.Name}} {{end}}

若只写{{range .Items}}不接变量,元素就绑定到.


8. 管道(pipeline)

管道用|,把左边结果作为右边函数的最后一个参数

{{.Name | printf "用户:%s"}}

等价于:printf("用户:%s", .Name)

多级管道

{{.Name | printf "%s" | html}}

(在html/template中,上下文感知转义通常已自动发生;这里主要演示管道形态。)

常见内置函数

函数作用例子
printf格式化{{printf "%.2f" .Price}}
len长度{{len .Items}}
index下标/键访问{{index .Items 0}}
slice切片(新版本支持){{slice .Items 0 3}}
and/or/not逻辑见上
urlquery查询参数转义{{.Q | urlquery}}
js/html按上下文转义较少手写

index详解

{{index .Scores 0}} <!-- slice[0] --> {{index .UserMap "u100"}} <!-- map["u100"] --> {{index .Matrix 1 2}} <!-- 多维:先 [1] 再 [2] -->

当字段名是动态字符串、或键不是合法标识符时,index很有用。

printf详解

{{printf "%s-%d" .Name .Age}} {{.Price | printf "¥%d"}}

9. 综合示例:商品列表页

main.go数据:

package main import ( "html/template" "os" ) type Product struct { Name string Price int Tags []string } type Page struct { Title string ShopName string VIP bool Items []Product } func main() { tmpl := template.Must(template.New("list").Parse(listHTML)) data := Page{ Title: "今日特价", ShopName: "七米小店", VIP: true, Items: []Product{ {Name: "Go 书", Price: 88, Tags: []string{"编程", "热销"}}, {Name: "键盘", Price: 299, Tags: []string{"外设"}}, }, } tmpl.Execute(os.Stdout, data) } const listHTML = ` <!DOCTYPE html> <html> <head><meta charset="utf-8"><title>{{.Title}}</title></head> <body> <h1>{{.Title}}</h1> <p>店铺:{{.ShopName}}</p> {{if .VIP}} <p>VIP 专属折扣已开启</p> {{else}} <p>开通 VIP 更优惠</p> {{end}} <p>共 {{len .Items}} 件商品</p> {{$shop := .ShopName}} <ul> {{range $i, $p := .Items}} <li> #{{$i}} [{{$shop}}] <strong>{{$p.Name}}</strong> {{printf "¥%d" $p.Price}} {{with $p.Tags}} 标签: {{range .}} <span>{{.}}</span> {{end}} {{end}} </li> {{else}} <li>暂无商品</li> {{end}} </ul> {{with index .Items 0}} <p>推荐:{{.Name}}</p> {{end}} </body> </html> `

这个例子一口气用到了:

  • 输出、iflen
  • $shop保留外层
  • range带索引
  • with处理标签
  • printf管道式格式化
  • index取第一件商品

建议你改数据:把Items置空、把VIPfalse,观察分支变化。


10. 空值与安全输出意识

指针字段

type User struct { Name *string }

Name == nil{{.Name}}可能出问题或为空。更稳妥是在 Go 侧先规范化数据,或模板里:

{{with .Name}}{{.}}{{else}}匿名{{end}}

HTML 转义

html/template会按插入点上下文转义。用户可控内容不要随便用template.HTML关闭转义(lesson08 会讲)。

缺失键

对 map,可设置:

tmpl.Option("missingkey=error")

让访问不存在的 key 直接报错,便于调试

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/3 16:20:51

表格驱动批量文档生成技术解析与实践

1. 项目概述&#xff1a;当批量文档生成遇上表格驱动在行政办公、教育管理、市场营销等场景中&#xff0c;我们经常遇到需要批量生成大量格式相似但内容不同的文档的需求。比如学校要给几百名学生生成个性化的成绩单&#xff0c;HR部门要给全体员工制作带个人信息的劳动合同&am…

作者头像 李华
网站建设 2026/8/3 16:20:41

Web安全实战:文件上传漏洞攻防全解析与绕过技巧

1. 项目概述&#xff1a;从靶场到实战的文件上传攻防演练在Web安全的学习与实战演练中&#xff0c;文件上传漏洞始终是一个绕不开的核心议题。它不像SQL注入那样需要复杂的逻辑构造&#xff0c;也不像XSS那样依赖用户交互&#xff0c;一个看似简单的“上传”按钮背后&#xff0…

作者头像 李华
网站建设 2026/8/3 16:17:59

工业变频器Modbus-RTU通信调试:从RS-485接线到组态配置全流程解析

1. 项目概述与核心需求解析最近在做一个产线改造项目&#xff0c;其中涉及到几台伟创AC310变频器的集中监控与数据采集。客户要求将变频器的运行状态、电流、频率等关键参数实时上传到中控室的上位机系统&#xff0c;实现远程启停和参数调整。这个需求在工业自动化领域非常典型…

作者头像 李华
网站建设 2026/8/3 16:14:52

C++通过ADO远程访问Oracle数据库:环境配置、核心代码与性能优化

1. 项目概述&#xff1a;为什么选择C与ADO进行Oracle远程访问&#xff1f; 在众多企业级应用的后台&#xff0c;尤其是那些运行在Windows平台上的历史遗留系统或高性能计算服务中&#xff0c;C与Oracle数据库的组合依然是一个经典且稳固的技术栈。当项目需求明确指向“远程访问…

作者头像 李华
网站建设 2026/8/3 16:14:45

热电联产系统优化:MATLAB建模与遗传算法实战

1. 热电联产系统规划的核心挑战 在区域能源规划中&#xff0c;热电联产&#xff08;Combined Heat and Power, CHP&#xff09;系统的选址与容量确定是个典型的多目标优化问题。我参与过三个工业园区的CHP系统设计&#xff0c;发现规划者常面临几个关键矛盾&#xff1a; 热/电…

作者头像 李华