夜雨聆风学习资料网

ARTICLE · 1329125

Go 编程实战:Gin 模板渲染——HTML 模板、动态数据、循环判断、布局与自定义函数

Go 编程实战:Gin 模板渲染——HTML 模板、动态数据、循环判断、布局与自定义函数

上一篇学习了 Gin Cookie,掌握了 Cookie 的读写、过期管理、跨域配置及安全实践。今天继续学习 Gin 模板渲染。在实际 Web 开发中,并不是所有项目都需要 Vue、React 等前端框架。对于企业官网、博客系统、内容管理平台和后台管理页面,使用 Go 服务端模板直接生成 HTML,也是一种简单高效的方案。Gin 基于 Go 标准库 html/template 实现模板渲染,支持动态变量、条件判断、循环、自定义函数和模板复用。

一、什么是模板渲染

模板渲染是将 HTML 模板与后端数据组合,生成完整网页的过程。

浏览器请求
    ↓
Gin 路由
    ↓
Handler 查询数据
    ↓
HTML 模板解析
    ↓
动态数据填充
    ↓
生成 HTML
    ↓
返回浏览器

例如文章详情页,HTML 结构基本固定,标题、作者、发布时间和正文由数据库提供。通过模板渲染,可以避免手动拼接 HTML 字符串。

二、创建项目

mkdir gin-template
cd gin-template
go mod init gin-template
go get github.com/gin-gonic/gin

创建目录:

gin-template/
├── main.go
├── templates/
│   ├── index.html
│   ├── article.html
│   └── layout/
│       └── header.html
└── static/
    ├── css/
    └── js/

其中 templates 存放 HTML 模板,static 存放 CSS、JavaScript 和图片。

三、加载 HTML 模板

Gin 使用 LoadHTMLGlob 加载模板:

package main

import (
    "log"

    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()
    r.LoadHTMLGlob("templates/*")

    r.GET("/", func(c *gin.Context) {
        c.HTML(200, "index.html", gin.H{
            "title": "Gin 模板首页",
            "name":  "Tom",
        })
    })

    log.Fatal(r.Run(":8080"))
}

创建 templates/index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>{{.title}}</title>
</head>
<body>
<h1>{{.title}}</h1>
<p>欢迎你,{{.name}}</p>
</body>
</html>

运行:

go run .

访问 http://localhost:8080,即可看到动态生成的页面。

四、理解模板变量

Go 模板使用 {{ }} 表示模板动作。

<h1>{{.title}}</h1>
<p>{{.description}}</p>

其中 . 表示当前数据上下文。Gin 通过 c.HTML() 将数据传入模板:

c.HTML(200, "index.html", gin.H{
    "title":       "Go 编程教程",
    "description": "学习 Gin Web 开发",
})

模板中的字段名称需要与传入的数据对应,否则可能出现空值或渲染错误。

五、使用结构体传递数据

除了 gin.H,也可以使用结构体:

type Article struct {
    ID          int64
    Title       string
    Description string
    Author      string
}

Handler:

article := Article{
    ID:          1001,
    Title:       "Gin 模板渲染教程",
    Description: "学习服务端 HTML 渲染",
    Author:      "Tom",
}
c.HTML(200, "article.html", article)

模板:

<h1>{{.Title}}</h1>
<p>{{.Description}}</p>
<span>作者:{{.Author}}</span>

结构体字段必须导出,即字段名称首字母大写,才能被模板正常访问。

六、条件判断 if

模板支持条件判断:

{{if .isLogin}}
<p>欢迎回来,{{.username}}</p>
{{else}}
<a href="/login">立即登录</a>
{{end}}

后端:

c.HTML(200, "index.html", gin.H{
    "isLogin":  true,
    "username": "Tom",
})

if 可以根据布尔值、空字符串、零值和空集合等条件决定是否显示内容。

七、循环遍历 range

文章列表经常需要循环渲染。
后端:

articles := []Article{
    {ID: 1, Title: "Go 入门"},
    {ID: 2, Title: "Gin 路由"},
    {ID: 3, Title: "Gin 模板"},
}
c.HTML(200, "index.html", gin.H{
    "articles": articles,
})

模板:

<ul>
{{range .articles}}
<li>
<a href="/article/{{.ID}}">{{.Title}}</a>
</li>
{{else}}
<li>暂无文章</li>
{{end}}
</ul>

range 可以遍历切片、数组和 Map,适合文章列表、商品列表和导航菜单。

八、获取循环索引

使用变量接收索引和元素:

{{range $index, $article := .articles}}
<p>{{$index}}:{{$article.Title}}</p>
{{end}}

模板变量以 $ 开头,适合在复杂循环中保存当前元素或外部上下文。

九、with 切换上下文

当页面包含嵌套数据时,可以使用 with:

{{with .user}}
<h2>{{.Name}}</h2>
<p>{{.Email}}</p>
{{else}}
<p>用户信息不存在</p>
{{end}}

with 会将当前上下文切换到 .user,减少重复编写字段路径。

十、模板比较运算

Go 模板提供内置比较函数:

{{if eq .status 1}}
<span>已发布</span>
{{else}}
<span>未发布</span>
{{end}}

常见函数:

eq:等于
ne:不等于
lt:小于
le:小于等于
gt:大于
ge:大于等于
and:逻辑与
or:逻辑或
not:逻辑非

例如:

{{if and .isLogin .isAdmin}}
<a href="/admin">后台管理</a>
{{end}}

注意,模板隐藏按钮不等于权限控制,服务端接口仍必须验证用户权限。

十一、模板复用 define

多个页面通常拥有相同的页头和页脚,可以使用 define 定义可复用模板。
创建 templates/layout/header.html:

{{define "header"}}
<header>
<h1>我的网站</h1>
<nav>
<a href="/">首页</a>
<a href="/articles">文章</a>
</nav>
</header>
{{end}}

页面中调用:

{{template "header" .}}
<main>
<h2>{{.title}}</h2>
</main>

这里的 . 表示将当前数据上下文继续传递给子模板。

十二、加载多级目录模板

当模板存在子目录时,可以使用:

r.LoadHTMLGlob("templates/**/*")

但这个模式主要匹配指定层级,不会自动递归匹配所有深度,也可能遗漏根目录文件。
对于目录结构固定的项目,可以显式列出模板文件:

r.LoadHTMLFiles(
    "templates/index.html",
    "templates/article.html",
    "templates/layout/header.html",
)

实际项目可以在启动阶段统一收集模板文件,再通过 SetHTMLTemplate 注册。

十三、自定义模板函数

Go 模板允许注册自定义函数,例如格式化时间:

func formatTime(t time.Time) string {
    return t.Format("2006-01-02 15:04:05")
}

注册:

r.SetFuncMap(template.FuncMap{
    "formatTime": formatTime,
})
r.LoadHTMLGlob("templates/*")

需要导入:

"html/template"
"time"

模板调用:

<p>发布时间:{{formatTime .PubDate}}</p>

注意,应先注册函数,再加载模板。

十四、格式化文章发布时间

定义文章:

type Article struct {
    Title   string
    PubDate time.Time
}

后端:

c.HTML(200, "article.html", Article{
    Title:   "Gin 模板教程",
    PubDate: time.Now(),
})

模板:

<h1>{{.Title}}</h1>
<time>{{formatTime .PubDate}}</time>

这种方式适合 CMS、博客和新闻网站。

十五、HTML 自动转义

Gin 使用 Go 的 html/template,默认会根据 HTML 上下文对动态内容进行安全转义。
例如用户输入:

<script>alert('test')</script>

直接使用:

<div>{{.content}}</div>

通常会被转义为文本,而不是作为脚本执行。
这是防止 XSS 的重要保护措施。

十六、渲染富文本 HTML

文章正文可能来自富文本编辑器,需要保留 HTML 标签。
Go 提供 template.HTML 类型:

content := template.HTML("<p>文章正文</p>")

模板:

<div class="article-content">{{.content}}</div>

但 template.HTML 会告诉模板引擎信任这段 HTML,不能直接用于未经处理的用户输入。
正确做法是先通过可靠的 HTML 清理器按允许的标签、属性和 URL 协议进行过滤,再将结果作为可信 HTML 渲染。

十七、模板布局设计

实际项目通常采用统一布局:

templates/
├── layouts/
│   ├── base.html
│   ├── header.html
│   └── footer.html
├── pages/
│   ├── home.html
│   ├── list.html
│   └── detail.html
└── components/
    ├── sidebar.html
    └── pagination.html

Go 标准模板没有传统面向对象意义上的继承,但可以通过 define、template 和 block 实现布局复用。
需要注意,不同页面如果定义相同的模板名称,直接全部解析到同一个模板集合可能发生覆盖,应按页面组织独立模板集合。

十八、结合数据库渲染

例如从数据库查询文章列表:

type Article struct {
    ID    int64
    Title string
}

func articleList(c *gin.Context) {
    articles := []Article{
        {ID: 1, Title: "Go 教程"},
        {ID: 2, Title: "Gin 实战"},
    }
    c.HTML(200, "index.html", gin.H{
        "title":    "文章列表",
        "articles": articles,
    })
}

实际项目中,可以通过 Service 层查询 MySQL,再将查询结果传递给模板。
建议将数据库查询、业务逻辑和模板渲染分开,避免 Handler 过于复杂。

十九、SEO 与服务端渲染

服务端模板渲染非常适合需要搜索引擎抓取的内容网站。
例如:

<head>
<title>{{.seoTitle}}</title>
<meta name="description" content="{{.seoDescription}}">
<link rel="canonical" href="{{.canonicalURL}}">
</head>

服务端可以为每篇文章生成独立的标题、描述和规范 URL。
同时需要正确处理分页、404 状态码和重复内容。模板渲染本身并不能保证搜索排名,但能够提供直接可读取的 HTML 内容。

二十、模板性能优化

模板渲染虽然方便,但高并发场景仍需要合理优化。
建议:

  1. 1. 在应用启动时解析模板,避免每次请求重复加载。
  2. 2. 减少模板中的复杂逻辑。
  3. 3. 数据库查询与 HTML 渲染分层。
  4. 4. 对热门页面使用缓存。
  5. 5. 为静态资源配置浏览器缓存。
  6. 6. 使用 Benchmark 和 pprof 分析性能。
    对于内容变化较少的页面,还可以考虑生成静态 HTML,进一步降低服务器开销。

二十一、常见问题

第一,模板文件路径错误。第二,结构体字段未导出。第三,使用 range 后误解当前数据上下文。第四,注册模板函数的顺序不正确。第五,直接将用户输入转换为 template.HTML。第六,多个页面重复定义同名模板造成覆盖。第七,在模板中编写大量业务逻辑。第八,忽略 HTML 自动转义。第九,误认为模板隐藏管理按钮就能实现权限控制。第十,每次请求重新解析模板导致额外性能开销。

二十二、总结

Gin 模板渲染的核心知识包括:

LoadHTMLGlob:加载模板文件
LoadHTMLFiles:指定模板文件
c.HTML:返回HTML页面
{{.field}}:输出动态变量
{{if}}:条件判断
{{range}}:循环遍历
{{with}}:切换上下文
{{define}}:定义模板
{{template}}:复用模板
SetFuncMap:注册自定义函数
html/template:HTML安全转义

对于博客、CMS、企业官网和管理后台,Gin 模板渲染能够以较低的开发复杂度实现动态网页。实际项目建议采用统一布局、组件复用和数据分层的方式组织模板,同时重视 HTML 转义、缓存和 SEO。下一篇将学习 Gin 静态资源管理,解决 CSS、JavaScript、图片、字体和下载文件的访问问题,并介绍缓存控制、资源压缩和生产环境部署。
下一篇:静态资源——Gin Static、StaticFS、静态文件服务、缓存控制、资源压缩、Embed 嵌入与 Nginx 部署实战

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料