模板基础 #

一、模板概述 #

1.1 什么是模板 #

模板是将动态数据嵌入到静态HTML中的技术:

text
HTML模板 + 数据 → 渲染后的HTML页面

1.2 Gin模板引擎 #

Gin使用Go标准库的 html/template 作为模板引擎:

go
import "html/template"

1.3 模板相关方法 #

方法 说明
LoadHTMLGlob 加载单个模式匹配的模板
LoadHTMLFiles 加载指定文件列表
SetFuncMap 设置模板函数
HTML 渲染HTML响应

二、加载模板 #

2.1 LoadHTMLGlob #

go
func main() {
    r := gin.Default()
    
    // 加载单个目录下所有模板
    r.LoadHTMLGlob("templates/*")
    
    r.GET("/", func(c *gin.Context) {
        c.HTML(200, "index.html", gin.H{
            "title": "首页",
        })
    })
    
    r.Run()
}

2.2 多级目录加载 #

go
func main() {
    r := gin.Default()
    
    // 加载多级目录模板
    r.LoadHTMLGlob("templates/**/*")
    
    r.GET("/", func(c *gin.Context) {
        c.HTML(200, "index.html", gin.H{})
    })
    
    r.GET("/user/profile", func(c *gin.Context) {
        c.HTML(200, "user/profile.html", gin.H{})
    })
    
    r.Run()
}

2.3 LoadHTMLFiles #

go
func main() {
    r := gin.Default()
    
    // 加载指定文件
    r.LoadHTMLFiles(
        "templates/index.html",
        "templates/about.html",
    )
    
    r.GET("/", func(c *gin.Context) {
        c.HTML(200, "index.html", gin.H{})
    })
    
    r.Run()
}

三、渲染模板 #

3.1 基本渲染 #

go
func main() {
    r := gin.Default()
    r.LoadHTMLGlob("templates/*")
    
    r.GET("/", func(c *gin.Context) {
        c.HTML(200, "index.html", gin.H{
            "title":   "首页",
            "message": "Hello, Gin!",
        })
    })
    
    r.Run()
}

templates/index.html:

html
<!DOCTYPE html>
<html>
<head>
    <title>{{ .title }}</title>
</head>
<body>
    <h1>{{ .message }}</h1>
</body>
</html>

3.2 传递复杂数据 #

go
type User struct {
    ID    uint
    Name  string
    Email string
}

func main() {
    r := gin.Default()
    r.LoadHTMLGlob("templates/*")
    
    r.GET("/user", func(c *gin.Context) {
        user := User{
            ID:    1,
            Name:  "Alice",
            Email: "alice@example.com",
        }
        
        c.HTML(200, "user.html", gin.H{
            "user": user,
        })
    })
    
    r.Run()
}

templates/user.html:

html
<!DOCTYPE html>
<html>
<head>
    <title>用户信息</title>
</head>
<body>
    <h1>用户信息</h1>
    <p>ID: {{ .user.ID }}</p>
    <p>姓名: {{ .user.Name }}</p>
    <p>邮箱: {{ .user.Email }}</p>
</body>
</html>

3.3 传递列表数据 #

go
func main() {
    r := gin.Default()
    r.LoadHTMLGlob("templates/*")
    
    r.GET("/users", func(c *gin.Context) {
        users := []User{
            {ID: 1, Name: "Alice", Email: "alice@example.com"},
            {ID: 2, Name: "Bob", Email: "bob@example.com"},
            {ID: 3, Name: "Charlie", Email: "charlie@example.com"},
        }
        
        c.HTML(200, "users.html", gin.H{
            "users": users,
        })
    })
    
    r.Run()
}

四、模板语法 #

4.1 变量输出 #

html
<!-- 输出变量 -->
{{ .title }}

<!-- 输出嵌套字段 -->
{{ .user.Name }}

<!-- 定义变量 -->
{{ $name := "Alice" }}
{{ $name }}

4.2 条件判断 #

html
<!-- if判断 -->
{{ if .user }}
    <p>用户: {{ .user.Name }}</p>
{{ end }}

<!-- if-else -->
{{ if .isLoggedIn }}
    <p>欢迎回来!</p>
{{ else }}
    <p>请先登录</p>
{{ end }}

<!-- if-else if-else -->
{{ if eq .status "active" }}
    <span class="active">激活</span>
{{ else if eq .status "inactive" }}
    <span class="inactive">未激活</span>
{{ else }}
    <span class="unknown">未知</span>
{{ end }}

4.3 循环遍历 #

html
<!-- 遍历切片 -->
{{ range .users }}
    <div>
        <p>ID: {{ .ID }}</p>
        <p>姓名: {{ .Name }}</p>
        <p>邮箱: {{ .Email }}</p>
    </div>
{{ end }}

<!-- 带索引遍历 -->
{{ range $index, $user := .users }}
    <div>
        <p>序号: {{ $index }}</p>
        <p>姓名: {{ $user.Name }}</p>
    </div>
{{ end }}

<!-- 遍历Map -->
{{ range $key, $value := .settings }}
    <p>{{ $key }}: {{ $value }}</p>
{{ end }}

4.4 管道操作 #

html
<!-- 链式操作 -->
{{ .name | upper }}

<!-- 多个管道 -->
{{ .content | truncate 100 | lower }}

4.5 模板注释 #

html
{{/* 这是注释,不会输出 */}}

{{/*
多行注释
不会被渲染
*/}}

五、模板继承 #

5.1 定义基础模板 #

templates/base.html:

html
<!DOCTYPE html>
<html>
<head>
    <title>{{ template "title" . }}</title>
    {{ block "head" . }}{{ end }}
</head>
<body>
    <header>
        {{ block "header" . }}
        <nav>
            <a href="/">首页</a>
            <a href="/about">关于</a>
        </nav>
        {{ end }}
    </header>
    
    <main>
        {{ block "content" . }}{{ end }}
    </main>
    
    <footer>
        {{ block "footer" . }}
        <p>&copy; 2024 My Website</p>
        {{ end }}
    </footer>
</body>
</html>

5.2 继承基础模板 #

templates/index.html:

html
{{ define "title" }}首页{{ end }}

{{ define "head" }}
<style>
    .hero { color: blue; }
</style>
{{ end }}

{{ define "content" }}
<div class="hero">
    <h1>欢迎来到首页</h1>
    <p>{{ .message }}</p>
</div>
{{ end }}

templates/about.html:

html
{{ define "title" }}关于我们{{ end }}

{{ define "content" }}
<div class="about">
    <h1>关于我们</h1>
    <p>这是一个关于页面</p>
</div>
{{ end }}

5.3 渲染继承模板 #

go
func main() {
    r := gin.Default()
    
    // 加载所有模板
    r.LoadHTMLGlob("templates/*")
    
    r.GET("/", func(c *gin.Context) {
        c.HTML(200, "base.html", gin.H{
            "message": "Hello, Gin!",
        })
    })
    
    r.GET("/about", func(c *gin.Context) {
        c.HTML(200, "base.html", gin.H{})
    })
    
    r.Run()
}

六、模板包含 #

6.1 定义子模板 #

templates/header.html:

html
{{ define "header" }}
<header>
    <nav>
        <a href="/">首页</a>
        <a href="/about">关于</a>
        <a href="/contact">联系</a>
    </nav>
</header>
{{ end }}

templates/footer.html:

html
{{ define "footer" }}
<footer>
    <p>&copy; 2024 My Website</p>
</footer>
{{ end }}

6.2 包含子模板 #

templates/layout.html:

html
<!DOCTYPE html>
<html>
<head>
    <title>{{ .title }}</title>
</head>
<body>
    {{ template "header" . }}
    
    <main>
        {{ template "content" . }}
    </main>
    
    {{ template "footer" . }}
</body>
</html>

6.3 使用template包含 #

html
<!-- 包含其他模板 -->
{{ template "header" . }}

<!-- 包含并传递数据 -->
{{ template "user_card" .user }}

七、模板布局 #

7.1 多布局支持 #

go
func main() {
    r := gin.Default()
    r.LoadHTMLGlob("templates/**/*")
    
    r.GET("/", func(c *gin.Context) {
        c.HTML(200, "layouts/main.html", gin.H{
            "content": "index.html",
            "title":   "首页",
        })
    })
    
    r.GET("/admin", func(c *gin.Context) {
        c.HTML(200, "layouts/admin.html", gin.H{
            "content": "admin/dashboard.html",
            "title":   "管理后台",
        })
    })
    
    r.Run()
}

7.2 动态布局 #

go
func renderWithLayout(c *gin.Context, content string, data gin.H) {
    layout := "layouts/main.html"
    
    if isAdmin(c) {
        layout = "layouts/admin.html"
    }
    
    data["content"] = content
    c.HTML(200, layout, data)
}

func main() {
    r := gin.Default()
    r.LoadHTMLGlob("templates/**/*")
    
    r.GET("/dashboard", func(c *gin.Context) {
        renderWithLayout(c, "dashboard.html", gin.H{
            "title": "仪表盘",
        })
    })
    
    r.Run()
}

八、错误处理 #

8.1 模板不存在 #

go
func main() {
    r := gin.Default()
    r.LoadHTMLGlob("templates/*")
    
    r.GET("/page", func(c *gin.Context) {
        defer func() {
            if err := recover(); err != nil {
                c.String(500, "模板渲染错误")
            }
        }()
        
        c.HTML(200, "nonexistent.html", gin.H{})
    })
    
    r.Run()
}

8.2 开发模式热重载 #

go
func main() {
    r := gin.Default()
    
    // 开发模式下每次请求重新加载模板
    if gin.Mode() == gin.DebugMode {
        r.Use(func(c *gin.Context) {
            r.LoadHTMLGlob("templates/*")
            c.Next()
        })
    } else {
        r.LoadHTMLGlob("templates/*")
    }
    
    r.Run()
}

九、总结 #

9.1 核心要点 #

要点 说明
加载模板 LoadHTMLGlob、LoadHTMLFiles
渲染模板 c.HTML()
模板继承 define、block、template
模板语法 变量、条件、循环

9.2 最佳实践 #

实践 说明
模板继承 使用base模板减少重复
模块化 拆分header、footer等组件
缓存 生产环境缓存模板
安全 使用html/template自动转义

9.3 下一步 #

现在你已经掌握了模板基础,接下来让我们学习 模板函数,了解自定义模板函数!

最后更新:2026-03-28