505 lines
10 KiB
Markdown
505 lines
10 KiB
Markdown
|
# [[Gin] HTML 的模板渲染与静态资源文件的加载](https://blog.csdn.net/JN_HoweveR/article/details/129714194)
|
|||
|
|
|||
|
作者的 Gin 框架学习是根据 B站视频 [Gin教程_Golang框架Gin入门实战教程](https://www.bilibili.com/video/BV1fA411F7aM/) 来学习的,对大地老师的评价不吹不捧,很喜欢其讲课风格,而且这个视频特别适合小白学习,强烈推荐。
|
|||
|
|
|||
|
## HTML 的模板渲染
|
|||
|
|
|||
|
### 全部模板放在一个目录里面的配置方法
|
|||
|
|
|||
|
首先在项目根目录中新建 templates 文件夹,然后在文件夹中新建 index.html
|
|||
|
|
|||
|
```html
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<title>GoWeb</title>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
|
|||
|
<h2>{{.title}}</h2>
|
|||
|
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
```
|
|||
|
|
|||
|
Gin 框架中使用 c.HTML 可以渲染模板,渲染模板前使用 LoadHTMLGlob() 或者 LoadHTMLFiles() 方法加载模板。
|
|||
|
|
|||
|
```go
|
|||
|
package main
|
|||
|
|
|||
|
import (
|
|||
|
"github.com/gin-gonic/gin"
|
|||
|
"net/http"
|
|||
|
)
|
|||
|
|
|||
|
func main() {
|
|||
|
r := gin.Default()
|
|||
|
// 配置模板的文件
|
|||
|
r.LoadHTMLGlob("templates/*")
|
|||
|
|
|||
|
r.GET("/", func(c *gin.Context) {
|
|||
|
c.HTML(http.StatusOK, "index.html", gin.H{
|
|||
|
"title": "首页",
|
|||
|
})
|
|||
|
})
|
|||
|
r.Run()
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 模板放在不同目录里面的配置方法
|
|||
|
|
|||
|
Gin 框架中如果不同目录下面有同名模板的话我们需要使用下面方法加载模板
|
|||
|
|
|||
|
定义模板的时候需要通过 define 定义名称
|
|||
|
|
|||
|
templates/admin/index.html
|
|||
|
|
|||
|
```html
|
|||
|
<!--相当于给模板定义一个名字 define end 成对出现-->
|
|||
|
{{ define "admin/index.html" }}
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
<title>Document</title>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<h1>后台模板</h1>
|
|||
|
<h3>{{.title}}</h3>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
{{ end }}
|
|||
|
```
|
|||
|
|
|||
|
templates/default/index.html
|
|||
|
|
|||
|
```html
|
|||
|
{{ define "default/index.html" }}
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
<title>Document</title>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<h1>前台模板</h1>
|
|||
|
<h3>{{.title}}</h3>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
{{ end }}
|
|||
|
```
|
|||
|
|
|||
|
业务逻辑
|
|||
|
|
|||
|
```go
|
|||
|
package main
|
|||
|
|
|||
|
import (
|
|||
|
"github.com/gin-gonic/gin"
|
|||
|
"net/http"
|
|||
|
)
|
|||
|
|
|||
|
func main() {
|
|||
|
r := gin.Default()
|
|||
|
// 配置模板的文件
|
|||
|
r.LoadHTMLGlob("templates/**/*")
|
|||
|
|
|||
|
r.GET("/", func(c *gin.Context) {
|
|||
|
c.HTML(http.StatusOK, "default/index.html", gin.H{
|
|||
|
"title": "前台首页",
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
r.GET("/admin", func(c *gin.Context) {
|
|||
|
c.HTML(http.StatusOK, "admin/index.html", gin.H{
|
|||
|
"title": "后台首页",
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
r.Run()
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
如果模板在多级目录里面的话需要这样配置 r.LoadHTMLGlob("templates/**/**/*") /**表示目录
|
|||
|
|
|||
|
## Gin 模板基本语法
|
|||
|
|
|||
|
### {{.}} 输出数据
|
|||
|
|
|||
|
模板语法都包含在{{和}}之间,其中 {{.}} 中的点表示当前对象。
|
|||
|
|
|||
|
当传入一个结构体对象时,可以根据.来访问结构体的对应字段
|
|||
|
|
|||
|
```go
|
|||
|
package main
|
|||
|
import (
|
|||
|
"net/http"
|
|||
|
"github.com/gin-gonic/gin"
|
|||
|
)
|
|||
|
type UserInfo struct {
|
|||
|
Name string
|
|||
|
Gender string
|
|||
|
Age int
|
|||
|
}
|
|||
|
func main() {
|
|||
|
router := gin.Default()
|
|||
|
router.LoadHTMLGlob("templates/**/*")
|
|||
|
user := UserInfo{
|
|||
|
Name: "张三",
|
|||
|
Gender: "男",
|
|||
|
Age: 18,
|
|||
|
}
|
|||
|
router.GET("/", func(c *gin.Context) {
|
|||
|
c.HTML(http.StatusOK, "default/index.html", map[string]interface{}{
|
|||
|
"title": "前台首页",
|
|||
|
"user": user,
|
|||
|
}
|
|||
|
})
|
|||
|
router.Run(":8080")
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
```html
|
|||
|
{{ define "default/index.html" }}
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
<title>Document</title>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<h1>前台模板</h1>
|
|||
|
<h3>{{.title}}</h3>
|
|||
|
<h4>{{.user.Name}}</h4>
|
|||
|
<h4>{{.user.Age}}</h4>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
{{end}}
|
|||
|
```
|
|||
|
|
|||
|
### 变量
|
|||
|
|
|||
|
在模板中声明变量,用来保存传入模板的数据或其他语句生成的结果。
|
|||
|
|
|||
|
```html
|
|||
|
<h4>{{$obj := .title}}</h4>
|
|||
|
<h4>{{$obj}}</h4>
|
|||
|
```
|
|||
|
|
|||
|
### 比较函数
|
|||
|
|
|||
|
布尔函数会将任何类型的零值视为假,其余视为真。
|
|||
|
|
|||
|
| 函数 | 说明 |
|
|||
|
|------|------|
|
|||
|
| eq | 如果 arg1 == arg2,则返回真 |
|
|||
|
| ne | 如果 arg1 != arg2,则返回真 |
|
|||
|
| lt | 如果 arg1 < arg2,则返回真 |
|
|||
|
| le | 如果 arg1 <= arg2,则返回真 |
|
|||
|
| gt | 如果 arg1 > arg2,则返回真 |
|
|||
|
| ge | 如果 arg1 >= arg2,则返回真 |
|
|||
|
|
|||
|
### 条件判断
|
|||
|
|
|||
|
```html
|
|||
|
{{if pipeline}}
|
|||
|
T1
|
|||
|
{{end}}
|
|||
|
|
|||
|
{{if pipeline}}
|
|||
|
T1
|
|||
|
{{else}}
|
|||
|
T0
|
|||
|
{{end}}
|
|||
|
|
|||
|
{{if pipeline}}
|
|||
|
T1
|
|||
|
{{else if pipeline}}
|
|||
|
T0
|
|||
|
{{end}}
|
|||
|
|
|||
|
// score > 60 返回及格,否则返回不及格
|
|||
|
{{if gt .score 60}}
|
|||
|
及格
|
|||
|
{{else}}
|
|||
|
不及格
|
|||
|
{{end}}
|
|||
|
```
|
|||
|
|
|||
|
### Range
|
|||
|
|
|||
|
使用 range 关键字进行遍历,有以下两种写法,其中 pipeline 的值必须是数组、切片、字典或者通道。
|
|||
|
|
|||
|
```html
|
|||
|
{{range $key,$value := .obj}}
|
|||
|
{{$value}}
|
|||
|
{{end}}
|
|||
|
|
|||
|
|
|||
|
<!--如果 pipeline 的值其长度为0,不会有任何输出-->
|
|||
|
{{range $key,$value := .obj}}
|
|||
|
{{$value}}
|
|||
|
{{else}}
|
|||
|
pipeline 的值其长度为0
|
|||
|
{{end}}
|
|||
|
|
|||
|
|
|||
|
r.GET("/", func(c *gin.Context) {
|
|||
|
c.HTML(http.StatusOK, "index.html", gin.H{
|
|||
|
"hobby": []string{"吃饭", "睡觉", "打游戏"},
|
|||
|
})
|
|||
|
})
|
|||
|
<ul>
|
|||
|
{{range $key,$value := .hobby}}
|
|||
|
<li>{{$key}}---{{$value}}</li>
|
|||
|
{{end}}
|
|||
|
</ul>
|
|||
|
```
|
|||
|
|
|||
|
### With
|
|||
|
|
|||
|
```go
|
|||
|
r.GET("/", func(c *gin.Context) {
|
|||
|
c.HTML(http.StatusOK, "index.html", gin.H{
|
|||
|
"article": &Article{
|
|||
|
Title: "标题",
|
|||
|
Desc: "描述",
|
|||
|
Content: "内容",
|
|||
|
},
|
|||
|
})
|
|||
|
})
|
|||
|
```
|
|||
|
|
|||
|
```html
|
|||
|
<p>{{.article.Title}}</p>
|
|||
|
<p>{{.article.Desc}}</p>
|
|||
|
<p>{{.article.Content}}</p>
|
|||
|
{{with .article}}
|
|||
|
{{.Title}}
|
|||
|
{{.Desc}}
|
|||
|
{{.Content}}
|
|||
|
{{end}}
|
|||
|
```
|
|||
|
|
|||
|
### 自定义模板函数
|
|||
|
|
|||
|
```go
|
|||
|
// 自定义模板函数 , 注意要把这个函数放在加载模板前
|
|||
|
r.SetFuncMap(template.FuncMap{
|
|||
|
"UnixToTime": UnixToTime,
|
|||
|
"Println": Println,
|
|||
|
})
|
|||
|
```
|
|||
|
|
|||
|
```go
|
|||
|
func UnixToTime(timestamp int) string {
|
|||
|
t := time.Unix(int64(timestamp), 0)
|
|||
|
return t.Format("2006-01-02 15:04:05")
|
|||
|
}
|
|||
|
func Println(str1 string, str2 string) string {
|
|||
|
fmt.Println("str1==>", str1, "str2==>", str2)
|
|||
|
return str1 + "---" + str2
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
```go
|
|||
|
package main
|
|||
|
|
|||
|
import (
|
|||
|
"fmt"
|
|||
|
"github.com/gin-gonic/gin"
|
|||
|
"html/template"
|
|||
|
"net/http"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
type Article struct {
|
|||
|
Title string `json:"title"`
|
|||
|
Desc string `json:"desc"`
|
|||
|
Content string `json:"content"`
|
|||
|
}
|
|||
|
|
|||
|
// UnixToTime 时间戳转换成日期
|
|||
|
func UnixToTime(timestamp int) string {
|
|||
|
t := time.Unix(int64(timestamp), 0)
|
|||
|
return t.Format("2006-01-02 15:04:05")
|
|||
|
}
|
|||
|
func Println(str1 string, str2 string) string {
|
|||
|
fmt.Println("str1==>", str1, "str2==>", str2)
|
|||
|
return str1 + "---" + str2
|
|||
|
}
|
|||
|
func main() {
|
|||
|
r := gin.Default()
|
|||
|
|
|||
|
// 自定义模板函数 , 注意要把这个函数放在加载模板前
|
|||
|
r.SetFuncMap(template.FuncMap{
|
|||
|
"UnixToTime": UnixToTime,
|
|||
|
"Println": Println,
|
|||
|
})
|
|||
|
|
|||
|
// 配置模板的文件
|
|||
|
r.LoadHTMLGlob("templates/*")
|
|||
|
// 配置静态web目录 第一个参数表示路由,第二个参数表示映射的路径.
|
|||
|
r.Static("/static", "./static")
|
|||
|
r.GET("/", func(c *gin.Context) {
|
|||
|
c.HTML(http.StatusOK, "index.html", gin.H{
|
|||
|
"title": "首页",
|
|||
|
"msg": "koya1",
|
|||
|
"score": 80,
|
|||
|
"hobby": []string{"吃饭", "睡觉", "打游戏"},
|
|||
|
"article": &Article{
|
|||
|
Title: "标题",
|
|||
|
Desc: "描述",
|
|||
|
Content: "内容",
|
|||
|
},
|
|||
|
"date": 1629423555,
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
news := &Article{
|
|||
|
Title: "新闻页面",
|
|||
|
Desc: "新闻描述",
|
|||
|
Content: "新闻详情",
|
|||
|
}
|
|||
|
r.GET("/news", func(c *gin.Context) {
|
|||
|
c.HTML(http.StatusOK, "news.html", gin.H{
|
|||
|
"title": "首页",
|
|||
|
"news": news,
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
r.Run()
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
模板里面的语法
|
|||
|
|
|||
|
```html
|
|||
|
{{UnixToTime .date}}
|
|||
|
<br>
|
|||
|
{{Println .title .msg}}
|
|||
|
```
|
|||
|
|
|||
|
### 嵌套 template
|
|||
|
|
|||
|
新建 templates/public/page_header.html
|
|||
|
|
|||
|
```html
|
|||
|
{{ define "public/page_header.html" }}
|
|||
|
<h1>这是一个头部</h1>
|
|||
|
{{end}}
|
|||
|
```
|
|||
|
|
|||
|
外部引入
|
|||
|
|
|||
|
* 引入的名字为 page_header.html 中定义的名字
|
|||
|
* 引入的时候注意到最后的点(.)
|
|||
|
|
|||
|
```html
|
|||
|
{{template "public/page_header.html" .}}
|
|||
|
```
|
|||
|
|
|||
|
在 index.html 当中使用
|
|||
|
|
|||
|
```html
|
|||
|
<!-- 相当于给模板定义一个名字 define end 成对出现-->
|
|||
|
{{ define "default/index.html" }}
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
<title>Document</title>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
{{template "default/page_header.html" .}}
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
{{end}
|
|||
|
```
|
|||
|
|
|||
|
### 静态文件的加载
|
|||
|
|
|||
|
当渲染的 HTML 文件中引用了静态文件时,需要配置静态 web 服务,在项目根目录下新建 static 文件夹,其下放一些静态资源文件
|
|||
|
|
|||
|
![静态文件的加载](img/[Gin]_HTML_的模板渲染与静态资源文件的加载/001.png)
|
|||
|
|
|||
|
r.Static("/static","./static") 第一个参数表示路由,第二个参数表示路径
|
|||
|
|
|||
|
```go
|
|||
|
package main
|
|||
|
|
|||
|
import (
|
|||
|
"fmt"
|
|||
|
"github.com/gin-gonic/gin"
|
|||
|
"html/template"
|
|||
|
"net/http"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
type Article struct {
|
|||
|
Title string `json:"title"`
|
|||
|
Desc string `json:"desc"`
|
|||
|
Content string `json:"content"`
|
|||
|
}
|
|||
|
|
|||
|
// UnixToTime 时间戳转换成日期
|
|||
|
func UnixToTime(timestamp int) string {
|
|||
|
t := time.Unix(int64(timestamp), 0)
|
|||
|
return t.Format("2006-01-02 15:04:05")
|
|||
|
}
|
|||
|
func Println(str1 string, str2 string) string {
|
|||
|
fmt.Println("str1==>", str1, "str2==>", str2)
|
|||
|
return str1 + "---" + str2
|
|||
|
}
|
|||
|
func main() {
|
|||
|
r := gin.Default()
|
|||
|
|
|||
|
// 自定义模板函数 , 注意要把这个函数放在加载模板前
|
|||
|
r.SetFuncMap(template.FuncMap{
|
|||
|
"UnixToTime": UnixToTime,
|
|||
|
"Println": Println,
|
|||
|
})
|
|||
|
|
|||
|
// 配置模板的文件
|
|||
|
r.LoadHTMLGlob("templates/*")
|
|||
|
// 配置静态web目录 第一个参数表示路由,第二个参数表示映射的路径.
|
|||
|
r.Static("/static", "./static")
|
|||
|
r.GET("/", func(c *gin.Context) {
|
|||
|
c.HTML(http.StatusOK, "index.html", gin.H{
|
|||
|
"title": "首页",
|
|||
|
"msg": "koya1",
|
|||
|
"score": 80,
|
|||
|
"hobby": []string{"吃饭", "睡觉", "打游戏"},
|
|||
|
"article": &Article{
|
|||
|
Title: "标题",
|
|||
|
Desc: "描述",
|
|||
|
Content: "内容",
|
|||
|
},
|
|||
|
"date": 1629423555,
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
news := &Article{
|
|||
|
Title: "新闻页面",
|
|||
|
Desc: "新闻描述",
|
|||
|
Content: "新闻详情",
|
|||
|
}
|
|||
|
r.GET("/news", func(c *gin.Context) {
|
|||
|
c.HTML(http.StatusOK, "news.html", gin.H{
|
|||
|
"title": "首页",
|
|||
|
"news": news,
|
|||
|
})
|
|||
|
})
|
|||
|
r.Run()
|
|||
|
}
|
|||
|
```
|