
本文介绍如何使用 Go 的 html/template 包,通过模板组合({{template}})机制,在多个 HTML 页面中统一维护 header 和 footer,避免重复代码,提升可维护性。
本文介绍如何使用 go 的 `html/template` 包,通过模板组合(`{{template}}`)机制,在多个 html 页面中统一维护 header 和 footer,避免重复代码,提升可维护性。
在 Go Web 开发中,保持页面结构一致性(如全局导航栏、页脚版权信息)是常见需求。Go 标准库的 html/template(或其底层 text/template)原生支持模板嵌套与命名模板复用,无需第三方库即可优雅实现“一次定义、多处引用”的布局管理。
✅ 核心实现方式:命名模板 + 模板组合
关键在于将 header 和 footer 定义为命名模板({{define "name"}}...{{end}}),再在主模板中通过 {{template "name" .}} 插入。所有模板需在解析时一并加载,Go 会自动合并定义。
? 文件组织建议(推荐)
templates/
├── base.tmpl # 主布局模板(含 {{template "header"}} 和 {{template "footer"}})
├── header.tmpl # 定义 {{define "header"}}
├── footer.tmpl # 定义 {{define "footer"}}
└── home.tmpl # 具体页面内容(可作为 .Content 传入)? 示例代码
1. header.tmpl
{{define "header"}}
<header class="site-header">
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
</header>
{{end}}2. footer.tmpl
{{define "footer"}}
<footer class="site-footer">
<p>© {{.Year}} My App. All rights reserved.</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/code/10533" title="YIXUNCMS中秋专版2.0.4"><img
src="https://img.php.cn/upload/webcode/000/000/020/176259780354248.jpg" alt="YIXUNCMS中秋专版2.0.4" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/code/10533" title="YIXUNCMS中秋专版2.0.4">YIXUNCMS中秋专版2.0.4</a>
<p>系统介绍:YIXUNCMS中专专版是易迅软件工作室在中秋节来临之即推出的专题模板建站系统,使用增强版后台管控系统,板板设计符合节日特点。易迅软件工作室恭祝全国人民中秋快乐。特别提示:由于网站页面的不同设计,部分后台功能未在前端进行体现。系统特点:1、采用目前流行的PHP语言编写,底层采用超轻量级框架作为系统支撑;2、页面布局使用DIV+CSS技术,遵循WEB标准,及大提高页面的浏览速度;3、使用应</p>
</div>
<a href="/xiazai/code/10533" title="YIXUNCMS中秋专版2.0.4" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div>
</footer>
{{end}}3. base.tmpl(主布局模板)
{{template "header" .}}
<main>
{{.Content | safeHTML}}
</main>
{{template "footer" .}}4. Go 后端渲染逻辑
func renderTemplate(w http.ResponseWriter, tmplName string, data interface{}) {
// 一次性解析所有模板文件(自动识别 define 块)
t := template.Must(template.ParseGlob("templates/*.tmpl"))
// 构造带动态数据的上下文(如当前年份)
ctx := struct {
Year int
Content template.HTML
}{
Year: time.Now().Year(),
Content: template.HTML("<h1>欢迎来到首页</h1><p>这是动态内容。</p>"),
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := t.ExecuteTemplate(w, "base.tmpl", ctx); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}? 提示:ParseGlob("templates/*.tmpl") 比 ParseFiles(...) 更易维护;使用 ExecuteTemplate(w, "base.tmpl", data) 显式指定入口模板,避免歧义。
⚠️ 注意事项与最佳实践
- 安全优先:html/template 默认自动转义变量输出。若需插入原始 HTML(如 .Content),务必使用 template.HTML 类型并配合 | safeHTML 过滤器,切勿对用户输入直接调用 safeHTML。
- 作用域继承:被 {{template}} 调用的命名模板共享父模板的 .(当前数据上下文),因此 {{.Year}} 在 header/footer 中同样可用。
- 错误处理:template.Must() 在解析失败时 panic,生产环境建议用 template.New("").ParseFiles(...) 并显式检查错误。
- 性能优化:模板应预编译(如在 init() 函数中解析),避免每次请求重复解析。
- 扩展性设计:可通过嵌套模板(如 {{template "content" .}})进一步解耦页面主体,实现真正的“布局-内容”分离。
通过上述方式,你不仅能实现问题中期望的 {{header}} ... {{content}} ... {{footer}} 结构,更能构建出可扩展、易测试、符合 Go 语言简洁哲学的模板系统。









