公司动态

Go 模板语法详解

📅 2026/8/3 16:24:33
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}}比较函数很常用这些是内置函数不是操作符写成函数含义eqne!ltlegtge{{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有两个作用判断值是否为真若为真在块内把.换成这个值{{with .Profile}} p城市{{.City}}/p p签名{{.Bio}}/p {{else}} p用户未填写资料/p {{end}}等价心智模型如果 Profile 有值 进入块且 . Profile 否则 走 elsewith 变量{{with $p : .Profile}} {{$p.City}} {{end}}ifvswithifwith是否切换.否是适合纯条件判断深入嵌套结构体字段很多人写{{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}} /ulelse分支在集合为空时执行非常适合空状态 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 headmeta charsetutf-8title{{.Title}}/title/head body h1{{.Title}}/h1 p店铺{{.ShopName}}/p {{if .VIP}} pVIP 专属折扣已开启/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 这个例子一口气用到了输出、if、len$shop保留外层range带索引with处理标签printf管道式格式化index取第一件商品建议你改数据把Items置空、把VIP改false观察分支变化。10. 空值与安全输出意识指针字段type User struct { Name *string }若Name nil{{.Name}}可能出问题或为空。更稳妥是在 Go 侧先规范化数据或模板里{{with .Name}}{{.}}{{else}}匿名{{end}}HTML 转义html/template会按插入点上下文转义。用户可控内容不要随便用template.HTML关闭转义lesson08 会讲。缺失键对 map可设置tmpl.Option(missingkeyerror)让访问不存在的 key 直接报错便于调试