Gin中局部模板必须用{{define}}和{{template}}配合,且需通过r.LoadHTMLGlob()加载才能生效;传参需显式指定(如{{template "row" .}}),否则上下文丢失导致不渲染。

用 {{define}} 和 {{template}} 定义局部模板
Gin 本身不提供额外的模板语法,直接复用 Go 标准库的 html/template,所以定义局部代码片段(即 partials)必须靠 {{define}} + {{template}} 配合使用。关键不是“怎么写”,而是“在哪解析”和“怎么传参”——模板只有被 r.LoadHTMLGlob() 或 r.LoadHTMLFiles() 加载后,{{define}} 才会被识别为命名模板,否则只是普通 HTML 注释。
- 局部模板名必须是合法标识符(不能含斜杠、空格、点号),比如
"user-card"可以,"users/list"不行 - 所有
{{define}}必须出现在被加载的 HTML 文件中(不能在 Go 代码里用字符串拼接后调用template.New().Parse()再注入 Gin,那样 Gin 不认) - 同一个文件里可以定义多个
{{define}},但名字不能重复;跨文件引用需确保全部文件都被LoadHTMLGlob加载(如"templates/**/*")
为什么 {{template "name"}} 有时不渲染?
最常见原因是:模板未被加载,或传参类型不匹配。Gin 的 c.HTML() 渲染时,只会把传入的 data(如 gin.H{})作为当前模板的 .,而 {{template}} 调用时默认也用这个 . 作为上下文——如果你没显式传参,它不会自动继承父作用域的变量。
- 正确传参写法:
{{template "row" .}}(把整个数据传给局部模板)、{{template "row" $item}}(前提是外层有{{$item := .Items}}或循环中{{range .Items}}{{template "row" .}}{{end}}) - 如果局部模板依赖 map 中某个字段,但传的是 struct,而 struct 没导出该字段(首字母小写),就会渲染为空——Go 模板只能访问导出字段
- 调试技巧:在局部模板开头加
{{printf "%#v" . | safe}}看实际收到什么,避免凭空猜测
如何组织 base.tmpl 和继承结构
Gin 不支持 {{block}} / {{end}} 的“块继承”语法(那是 text/template 的高级用法,html/template 不推荐用于 HTML 安全场景),但可以用 {{define}} + {{template}} 模拟。典型做法是:一个 base.tmpl 定义通用骨架和若干 {{define}} 占位,其他页面文件只写 {{define "main"}} 内容,再通过主模板统一 {{template "main"}}。
-
base.tmpl必须被LoadHTMLGlob加载(哪怕它不直接用于c.HTML()),否则其中的{{define}}不生效 - 子模板文件(如
users/index.html)里不要写或 <code>,只写{{define "main"}}内容块,否则会嵌套出错 - 渲染时调用
c.HTML(200, "base.tmpl", data),不是子模板名——Gin 不会自动查找并组合,必须手动指定入口模板
自定义函数对局部模板的影响
局部模板能用全局注册的自定义函数(如 safe、datefmt),但要注意:这些函数是在模板解析阶段绑定的,不是运行时动态注入。一旦 r.SetFuncMap() 设置完成,所有后续加载的模板都共享同一份函数表。
- 函数必须返回
template.HTML才能绕过自动转义,否则{{.Content}}中的<script>会被当成纯文本显示 - 别在局部模板里试图修改传入的数据(如
{{$.User.Name = "new"}}),Go 模板不支持赋值语句,这行代码会静默失败 - 性能提示:频繁调用复杂函数(如数据库查询封装)会导致模板渲染变慢,应提前计算好结果传入
gin.H{}


















