注:本教程使用工作区方法管理项目,详见第一讲:创建一个简单的Gin应用。
目录
- 一、简单渲染
- 二、使用不同目录下名称相同的模板
- 三、自定义模板渲染器
- 四、自定义分隔符
- 五、自定义模板函数
- 六、总结
一、简单渲染
首先,以03HTML渲染为根目录,创建如下目录结构:
├─demo01
│ │ main.go
│ └─templates
│ index.html
├─demo02
│ │ main.go
│ └─templates
│ ├─posts
│ │ index.html
│ └─users
│ index.html
├─demo03
│ file1.html
│ file2.html
│ main.go
├─demo04
│ │ main.go
│ └─templates
│ index.html
└─demo05│ main.go└─templatesraw.html
进入到demo01,填充代码:
main.go
package mainimport ("net/http" // 导入 HTTP 状态码和服务功能"github.com/gin-gonic/gin" // 导入 Gin 框架
)func main() {router := gin.Default() // 创建一个默认的 Gin 路由器,带有默认的中间件(如日志和恢复)// 加载所有位于 /templates 目录下的 HTML 文件,支持通配符(如 *.html)router.LoadHTMLGlob("templates/*")// 使用 LoadHTMLFiles 可以加载指定的多个文件// router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")// 设置一个 GET 路由,当用户访问 /index 时,返回 HTML 页面router.GET("/index", func(c *gin.Context) {// 使用 HTML 渲染函数,HTTP 状态码 200 (http.StatusOK),// 渲染 "index.html" 文件,并传递数据(键值对形式)供模板使用c.HTML(http.StatusOK, "index.html", gin.H{"title": "Main website", // 传递 "title" 变量到模板})})// 运行应用程序,并监听在端口 8080 上router.Run(":8080")
}
index.html
<html>
<h1>{{ .title }} <!-- 这里是模板占位符,将被渲染时替换为 title 变量的值 -->
</h1></html>
LoadHTMLGlob()
:用于加载模板文件的函数。LoadHTMLGlob("../templates/*")
表示加载 …/templates 目录下的所有 HTML 文件。它支持通配符,常用于一次性加载多个模板。
LoadHTMLFiles()
:和 LoadHTMLGlob() 类似,但它不使用通配符,而是明确列出要加载的模板文件。可以根据需要使用该函数加载特定的模板文件。
c.HTML(http.StatusOK, "index.html", gin.H{"title": "Main website"})
:渲染 index.html 模板,并将 title 变量传递给模板,值为 “Main website”。在模板文件中,{{ .title }}
会被渲染为传递进来的 title 值。
注意:Gin 的模板引擎支持多种文件扩展名,常见的 .html
文件扩展名并不是必须的,比如你可以将模板文件命名为 .tmpl
,模板引擎同样能够识别和渲染这些文件。但个人建议仍使用.html,因为IDE都能对.html提供代码高亮和补全。
效果:
二、使用不同目录下名称相同的模板
进入到demo02,填充代码:
main.go
package mainimport ("net/http" // 导入 HTTP 状态码和服务功能"github.com/gin-gonic/gin" // 导入 Gin 框架
)func main() {router := gin.Default() // 创建一个默认的 Gin 路由器,带有默认中间件(如日志和恢复)// 加载 templates 目录下所有子目录的所有模板文件router.LoadHTMLGlob("templates/**/*")// 定义一个 GET 路由,处理 /posts/indexrouter.GET("/posts/index", func(c *gin.Context) {// 渲染 posts/index.html 模板,并传递数据c.HTML(http.StatusOK, "posts/index.html", gin.H{"title": "Posts", // 传递标题})})// 定义另一个 GET 路由,处理 /users/indexrouter.GET("/users/index", func(c *gin.Context) {// 渲染 users/index.html 模板,并传递数据c.HTML(http.StatusOK, "users/index.html", gin.H{"title": "Users", // 传递标题})})// 启动 HTTP 服务器,监听 8080 端口router.Run(":8080")
}
posts/index.html
{{ define "posts/index.html" }} <!-- 定义一个名为 "posts/index.html" 的模板块 -->
<html>
<h1>{{ .title }} <!-- 这里会被传递进来的标题替换 -->
</h1>
<p>Using posts/index.html</p> <!-- 说明当前使用的是哪个模板 --></html>
{{ end }} <!-- 结束模板块定义 -->
users/index.html
{{ define "users/index.html" }} <!-- 定义一个名为 "users/index.html" 的模板块 -->
<html>
<h1>{{ .title }} <!-- 这里会被传递进来的标题替换 -->
</h1>
<p>Using users/index.html</p> <!-- 说明当前使用的是哪个模板 --></html>
{{ end }} <!-- 结束模板块定义 -->
模板定义:每个模板都使用 {{ define "模板名称" }}
和 {{ end }}
包裹,定义了一个可重用的模板块。在渲染时,通过指定模板的完整名称来选择要渲染的具体模板。
这使得在不同目录中存在同名模板时(比如这里都有index.html),可以准确指定使用哪个模板。当然,使用LoadHTMLFiles()
可以不用定义模板块,也能达到相同的效果,就是要指定具体文件路径而已。相对来说,LoadHTMLGlob()
使用更方便,不管templates下有多少个模板,调用语句都是一样的。
效果:
三、自定义模板渲染器
进入到demo03,填充代码:
main.go
package mainimport ("html/template" // 导入 HTML 模板包"net/http""github.com/gin-gonic/gin" // 导入 Gin 框架
)func main() {router := gin.Default()// 加载 file1.html 和 file2.html 模板文件html := template.Must(template.ParseFiles("file1.html", "file2.html"))// 设置自定义的 HTML 模板渲染器router.SetHTMLTemplate(html)// 定义一个路由,当用户访问 /file1 时,返回 file1.html 页面router.GET("/file1", func(c *gin.Context) {c.HTML(http.StatusOK, "file1.html", gin.H{"title": "Hello, World", // 传递 "title" 变量到模板})})// 定义另一个路由,当用户访问 /file2 时,返回 file2.html 页面router.GET("/file2", func(c *gin.Context) {c.HTML(http.StatusOK, "file2.html", gin.H{"title": "Hello, World", // 传递 "title" 变量到模板})})// 启动 Gin 服务器,监听在 8080 端口router.Run(":8080")
}
file1.html
<!DOCTYPE html>
<html><head><title>{{ .title }} - File 1</title>
</head><body><h1>{{ .title }} from File 1</h1><p>This content is rendered from file1.html.</p>
</body></html>
file2.html
<!DOCTYPE html>
<html><head><title>{{ .title }} - File 2</title>
</head><body><h1>{{ .title }} from File 2</h1><p>This content is rendered from file2.html.</p>
</body></html>
效果:
到目前为止,我们已经学了三种模板加载方法:
1.定义模板名,用LoadHTMLGlob()
2.不定义模板名,用LoadHTMLFiles()
3.不用模板目录,用template.Must()
在真实开发中,团队和项目的性质通常会影响选择的方式。常见的倾向包括:
中大型项目:往往会选择 1,因为它能保持项目结构的整洁,并且方便管理多个模板。
小型项目:可能会倾向于 2,因为它提供了更大的灵活性和可控性。
原型开发或快速迭代:通常会选择 3,以便于快速验证和调整。
四、自定义分隔符
进入到demo04,填充代码:
main.go
package mainimport ("net/http""github.com/gin-gonic/gin"
)func main() {r := gin.Default()// 设置自定义的分隔符r.Delims("{[{", "}]}")// 加载指定路径下的 HTML 模板r.LoadHTMLGlob("templates/*")// 定义一个路由,当用户访问 /index 时,返回 index.html 页面r.GET("/index", func(c *gin.Context) {c.HTML(http.StatusOK, "index.html", gin.H{"title": "Custom Delimiter Example", // 传递 "title" 变量到模板})})// 启动 Gin 服务器,监听在 8080 端口r.Run(":8080")
}
index.html
<!DOCTYPE html>
<html><head><title>{[{ .title }]}</title>
</head><body><h1>{[{ .title }]}</h1><p>Welcome to the page with custom delimiters!</p>
</body></html>
效果:
五、自定义模板函数
进入到demo05,填充代码:
main.go
package mainimport ("fmt""html/template""net/http""time""github.com/gin-gonic/gin"
)// 自定义函数:将时间格式化为指定格式
func formatAsDate(t time.Time) string {year, month, day := t.Date()return fmt.Sprintf("%d/%02d/%02d", year, month, day)
}func main() {router := gin.Default()router.Delims("{[{", "}]}") // 设置自定义分隔符// 注册自定义函数router.SetFuncMap(template.FuncMap{"formatAsDate": formatAsDate, // 将 formatAsDate 函数注册为模板函数})// 加载 HTML 模板文件router.LoadHTMLFiles("templates/raw.html")// 定义路由,当用户访问 /raw 时,返回 raw.html 模板router.GET("/raw", func(c *gin.Context) {c.HTML(http.StatusOK, "raw.html", map[string]interface{}{"now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), // 传递当前时间到模板})})// 启动 Gin 服务器,监听在 8080 端口router.Run(":8080")
}
raw.html
Date: {[{.now | formatAsDate}]}
注意:
1.FuncMap()
中的key和value可以不同。
2.html中调用的函数名是FuncMap()
中的key,不是value!也就是说,value的值可以随便取,不影响html调用。
3.SetFuncMap
必须在 LoadHTMLFiles
之前调用!
效果:
六、总结
在使用 Gin 框架进行 HTML 渲染时,可以通过 LoadHTMLGlob() 或 LoadHTMLFiles() 方法加载模板文件。LoadHTMLGlob() 支持使用通配符,适合批量加载多个模板,而 LoadHTMLFiles() 允许精确加载特定的模板文件。通过在模板中定义结构,可以避免不同目录中同名模板的冲突,确保在渲染时通过完整名称来选择正确的模板。自定义模板渲染器和函数映射(使用 SetFuncMap)可以扩展模板功能,如格式化日期。使用自定义分隔符可改变模板中变量的标识符。