虽然现在前后端分离很流行,但很多场景下服务端渲染 HTML 仍然是刚需。Gin 对 Go 标准库的
html/template做了封装,用起来很方便。
首先需要告诉 Gin 你的模板文件在哪里:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("templates/*")
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{
"title": "首页",
"message": "欢迎来到我的网站",
})
})
r.Run(":8080")
}
LoadHTMLGlob 支持通配符,templates/* 会加载 templates 目录下所有文件。
对于复杂项目,模板通常会分层组织:
templates/
├── layouts/
│ └── base.html
├── partials/
│ ├── header.html
│ └── footer.html
└── pages/
├── index.html
└── about.html
加载时使用 ** 递归匹配:
r.LoadHTMLGlob("templates/**/*")
在模板文件中定义布局:
layouts/base.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{ .title }}</title>
</head>
<body>
{{ template "partials/header" . }}
<main>
{{ block "content" . }}{{ end }}
</main>
{{ template "partials/footer" . }}
</body>
</html>
partials/header.html:
{{ define "partials/header" }}
<header>
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
</header>
{{ end }}
partials/footer.html:
{{ define "partials/footer" }}
<footer>
<p>© 2024 我的网站</p>
</footer>
{{ end }}
pages/index.html:
{{ define "content" }}
<h1>{{ .message }}</h1>
<p>这是首页内容</p>
{{ end }}
渲染时指定基础模板:
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "layouts/base.html", gin.H{
"title": "首页",
"message": "欢迎访问",
})
})
在处理函数中传递数据:
r.GET("/user/:id", func(c *gin.Context) {
user := struct {
ID int
Name string
Email string
}{
ID: 1,
Name: "张三",
Email: "zhangsan@example.com",
}
c.HTML(http.StatusOK, "user.html", gin.H{
"title": "用户详情",
"user": user,
})
})
模板中访问:
<h1>用户:{{ .user.Name }}</h1>
<p>邮箱:{{ .user.Email }}</p>
<p>ID:{{ .user.ID }}</p>
模板支持条件判断:
{{ if .user.IsAdmin }}
<span class="badge">管理员</span>
{{ else }}
<span class="badge">普通用户</span>
{{ end }}
循环遍历:
<ul>
{{ range .users }}
<li>{{ .Name }} - {{ .Email }}</li>
{{ end }}
</ul>
Gin 允许注册自定义函数:
r.SetFuncMap(template.FuncMap{
"safe": func(str string) template.HTML {
return template.HTML(str)
},
"upper": func(str string) string {
return strings.ToUpper(str)
},
"formatDate": func(t time.Time) string {
return t.Format("2006-01-02 15:04:05")
},
})
r.LoadHTMLGlob("templates/*")
在模板中使用:
<p>{{ .content | safe }}</p>
<p>{{ .name | upper }}</p>
<p>{{ .createdAt | formatDate }}</p>
注意:SetFuncMap 要在 LoadHTMLGlob 之前调用。
如果需要加载不同目录的模板:
r.LoadHTMLFiles(
"templates/index.html",
"templates/about.html",
"templates/contact.html",
)
开发模式下,每次修改模板都希望立即生效:
func main() {
r := gin.Default()
if gin.Mode() == gin.DebugMode {
r.LoadHTMLGlob("templates/**/*")
}
r.GET("/reload", func(c *gin.Context) {
r.LoadHTMLGlob("templates/**/*")
c.String(http.StatusOK, "模板已重新加载")
})
r.Run(":8080")
}
生产环境建议在启动时一次性加载,不要热重载。
Gin 的 HTML 模板功能基于 Go 标准库,支持模板继承、自定义函数等特性。对于传统的服务端渲染应用,这些功能完全够用。如果项目复杂度较高,也可以考虑集成第三方模板引擎如 Pongo2。