Gin无内置“公共模板”概念,所有模板平级加载,公共模板需手动通过{{define}}和{{template}}实现;LoadHTMLGlob仅解析文件内容,不识别或分类模板名,名称必须显式定义且严格一致。

Gin 里没有“公共模板”这个内置概念,所有模板都是平级加载的;所谓“公共模板”,其实是靠 template 标准库的 {{define}} + {{template}} 机制手动组织出来的。
为什么 LoadHTMLGlob("templates/*") 不会自动识别公共模板
Gin 的 LoadHTMLGlob 只负责把文件内容读进内存并解析成 *template.Template 对象,它不关心你写的是 {{define "header"}} 还是 {{define "user/list"}}。这些 define 名称只是你在模板里自己起的逻辑名,Gin 不做分类、不建层级、不自动注入。
- 如果你没在模板里显式写
{{define "layout.html"}},那它就只是一个普通模板文件,无法被{{template "layout.html" .}}调用 - 多个模板共用同一份 header,必须靠
{{template "header" .}}显式引入,而不是靠目录结构或文件名前缀 -
LoadHTMLGlob("templates/**/*")能加载子目录下的文件,但不会改变模板命名空间——admin/index.html和user/index.html是两个独立名字,不是“admin 下的 index”
{{define}} 和 {{template}} 必须成对出现且名称严格一致
这是最容易出错的地方:名字拼错、大小写不一致、漏写引号,都会导致 template: ...: "xxx" is not defined 错误。
- 定义时:
{{ define "base.html" }}<html>...{{ end }}—— 名称必须加双引号,且不能含路径("layouts/base.html"是非法的) - 调用时:
{{ template "base.html" . }}—— 名称必须和define中完全一致,包括空格和标点 - 传参必须是
.或明确结构体字段,不能写{{template "header" $}}($在 Gin 模板中不可用) - 如果想让子模板覆盖某一块区域,要用
{{block "main" .}}{{end}}+{{define "main"}}...{{end}}配合
多级目录下模板名称冲突怎么办
当 templates/admin/user/edit.html 和 templates/api/user/edit.html 同时存在时,LoadHTMLGlob("templates/**/*") 会把它们都加载进来,但默认都以文件名(不含路径)作为模板名,导致后加载的覆盖先加载的——最终只有最后一个 edit.html 可用。
- 解决方法:每个模板开头必须用
{{ define "admin/user/edit.html" }}显式声明唯一名称 - 加载时仍用
r.LoadHTMLGlob("templates/**/*"),Gin 会按define名注册,而非文件名 - 渲染时也必须用完整名称:
c.HTML(200, "admin/user/edit.html", data) - 别依赖 “自动推导路径”,Gin 不做这层映射;名称就是名称,和磁盘路径无关
静态资源与模板路径是两套独立系统
很多人以为把 static/css/app.css 放好,模板里写 <link href="/css/app.css"> 就能自动生效——其实不会。Gin 默认不提供任何静态服务,必须显式配置 r.Static() 或 r.StaticFS()。
- 模板里引用的路径(如
/css/app.css)是浏览器发起的 HTTP 请求路径,和 Go 项目目录结构无关 - 要让该请求返回真实文件,得配:
r.Static("/css", "./static/css"),表示把 URL 路径/css/xxx映射到磁盘./static/css/xxx - 如果模板里用了相对路径(如
../img/logo.png),浏览器会按当前 URL 解析,极易 404;统一用根路径/img/logo.png更可控 -
StaticFS更适合嵌入式部署(比如打包进二进制),但开发期用Static更直观
真正麻烦的不是语法,而是模板名、define 名、c.HTML() 第二个参数、静态路由前缀这四者之间的映射关系——它们之间没有任何自动绑定,全靠人脑维护一致。一旦某处改名,三处不跟着同步,页面就白屏或报错,而且错误提示往往不指明具体哪一行模板出了问题。


















