💜 想真正擅长编程? 从一门课程开始,100% 免费

《笔记》第二十六篇 Gin的公共模板嵌套

第26讲 Gin的公共模板嵌套

本讲基于视频实际讲解内容整理


一、本讲概述

上一讲我们学会了渲染单个 HTML 页面。但一个网站有很多页面,每个页面都要写一遍 <head>、导航栏、页脚,会产生大量重复代码。本讲讲解如何用 Go 模板的嵌套/继承机制,把公共部分抽成"公共模板",让每个页面只写自己独有的内容。


二、为什么需要公共模板

2.1 重复代码的痛点

没有公共模板时,每个页面都要重复写:

HTML
<!-- index.html -->
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>BBS</title></head>
<body>
    <nav>导航栏...</nav>
    <div>首页内容</div>
    <footer>页脚...</footer>
</body>
</html>

<!-- login.html -->
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>BBS</title></head>
<body>
    <nav>导航栏...</nav>      <!-- 又写了一遍 -->
    <div>登录表单</div>
    <footer>页脚...</footer>  <!-- 又写了一遍 -->
</body>
</html>

老师指出:导航栏、页脚在每个页面都一样,复制粘贴既臃肿又难维护。改一处要改所有页面,很容易漏。

2.2 目标:抽公共、留个性

把 <head>、<nav>、<footer> 抽到公共模板,每个页面只写中间不同的内容。


三、Go 模板的 define 与 template

3.1 define:定义一个命名模板

用 {{define "名字"}}...{{end}} 可以把一段模板定义为命名块:

HTML
<!-- templates/base.html -->
{{define "base"}}
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>BBS</title></head>
<body>
    <nav>导航栏...</nav>
    {{template "content" .}}   <!-- 在这里嵌入子模板 -->
    <footer>页脚...</footer>
</body>
</html>
{{end}}

3.2 template:引用(嵌套)另一个模板

{{template "content" .}} 表示"在这里插入名为 content 的模板",. 表示把当前数据传进去。

HTML
<!-- templates/index.html -->
{{define "content"}}
    <h1>首页内容</h1>
    <p>欢迎访问 BBS</p>
{{end}}

{{template "base" .}}   <!-- 引入公共布局,并把数据透传 -->

老师说明:{{template "base" .}} 让 index.html 整体套用 base 布局,而 base 里的 {{template "content" .}} 又会找当前文件(或同批加载的模板)里定义的 content。公共部分和个性内容就拼起来了。


四、用 block 定义可覆盖的默认块

4.1 block 的作用

{{block "名字" .}}默认内容{{end}} 和 template 类似,但如果子模板没有提供该块,就使用默认内容:

HTML
<!-- templates/base.html -->
{{define "base"}}
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>{{block "title" .}}BBS默认标题{{end}}</title>
</head>
<body>
    <nav>导航栏...</nav>
    {{block "content" .}}<p>默认内容</p>{{end}}
    <footer>页脚...</footer>
</body>
</html>
{{end}}

4.2 子页面覆盖块

HTML
<!-- templates/login.html -->
{{define "title"}}用户登录{{end}}
{{define "content"}}
    <h1>登录</h1>
    <form method="post" action="/login">
        用户名:<input name="username"><br>
        密码:<input type="password" name="password"><br>
        <button type="submit">登录</button>
    </form>
{{end}}

{{template "base" .}}

老师强调:block 适合做"可选覆盖"。比如 title 大部分页面用默认的,登录页想改标题就自己 define "title" 覆盖掉。这样公共模板更灵活。


五、完整示例

5.1 目录结构

TEXT
templates/
├── base.html     ← 公共布局(head/nav/footer)
├── index.html    ← 首页(只写 content)
└── login.html    ← 登录页(只写 content + title)

5.2 handler 中照常渲染

Go
func Index(c *gin.Context) {
    c.HTML(200, "index.html", gin.H{
        "username": "张三",
    })
}

func LoginPage(c *gin.Context) {
    c.HTML(200, "login.html", nil)
}
HTML
<!-- templates/index.html -->
{{define "content"}}
    <h1>欢迎 {{.username}}</h1>
    <p>这是 BBS 首页</p>
{{end}}
{{template "base" .}}

注意:因为是 LoadHTMLGlob("templates/*") 一次性加载所有模板,base、content、title 这些命名块彼此都能互相找到,嵌套才能生效。


六、本讲小结

知识点 要点
痛点 每个页面重复 head/nav/footer
define {{define "名"}}...{{end}} 定义命名模板
template {{template "名" .}} 嵌入另一个模板
block 可覆盖的默认块,子页不定义就用默认
公共布局 base.html 放公共结构,页面只写 content
渲染 handler 仍用 c.HTML(200, "页面.html", 数据)

七、课后练习

  1. 新建 templates/base.html,把 <head>、导航栏、页脚放进 {{define "base"}} 中
  2. 在 base 中用 {{block "content" .}} 预留内容区,用 {{block "title" .}} 预留标题
  3. 新建 index.html 和 login.html,各自只 define "content",最后 {{template "base" .}}
  4. 让 login.html 额外 define "title" 覆盖默认标题
  5. 在 main.go 中用 LoadHTMLGlob("templates/*") 加载,并渲染两个页面验证嵌套效果
  6. 思考:如果再增加一个"注册页",需要复制多少代码?公共模板带来了什么好处?