模板函数 #
一、模板函数概述 #
1.1 什么是模板函数 #
模板函数是在模板中可以调用的函数,用于处理数据:
html
{{ .name | upper }}
{{ formatDate .date "2006-01-02" }}
1.2 函数类型 #
| 类型 | 说明 | 示例 |
|---|---|---|
| 内置函数 | Go模板自带 | len、printf |
| 自定义函数 | 用户定义 | formatDate |
| 第三方函数 | 引入库函数 | strings.ToUpper |
二、内置模板函数 #
2.1 常用内置函数 #
html
<!-- and/or -->
{{ if and .condition1 .condition2 }}
两个条件都为真
{{ end }}
{{ if or .condition1 .condition2 }}
至少一个条件为真
{{ end }}
<!-- not -->
{{ if not .isLoggedIn }}
未登录
{{ end }}
<!-- eq/ne/lt/le/gt/ge -->
{{ if eq .status "active" }}激活{{ end }}
{{ if ne .status "inactive" }}非未激活{{ end }}
{{ if gt .age 18 }}成年人{{ end }}
{{ if lt .age 18 }}未成年{{ end }}
<!-- len -->
<p>列表长度: {{ len .items }}</p>
<!-- printf -->
<p>{{ printf "%s - %d" .name .age }}</p>
<!-- index -->
<p>第一个元素: {{ index .items 0 }}</p>
2.2 字符串函数 #
html
<!-- 打印 -->
{{ print .name }}
{{ println .name }}
{{ printf "%s: %d" .name .age }}
2.3 类型转换 #
html
<!-- js -->
<script>
var name = {{ .name | js }};
</script>
<!-- html -->
{{ .content | html }}
<!-- urlquery -->
<a href="/search?q={{ .query | urlquery }}">搜索</a>
三、注册自定义函数 #
3.1 SetFuncMap #
go
func main() {
r := gin.Default()
// 设置模板函数
r.SetFuncMap(template.FuncMap{
"upper": strings.ToUpper,
"lower": strings.ToLower,
"formatDate": formatDate,
})
r.LoadHTMLGlob("templates/*")
r.GET("/", func(c *gin.Context) {
c.HTML(200, "index.html", gin.H{
"name": "alice",
"date": time.Now(),
})
})
r.Run()
}
func formatDate(t time.Time, layout string) string {
return t.Format(layout)
}
3.2 在模板中使用 #
html
<!-- 使用自定义函数 -->
<p>大写: {{ .name | upper }}</p>
<p>小写: {{ .name | lower }}</p>
<p>日期: {{ formatDate .date "2006-01-02" }}</p>
四、常用自定义函数 #
4.1 字符串处理 #
go
func main() {
r := gin.Default()
r.SetFuncMap(template.FuncMap{
"upper": strings.ToUpper,
"lower": strings.ToLower,
"title": strings.Title,
"trim": strings.TrimSpace,
"substr": substr,
"truncate": truncate,
"replace": replace,
})
r.LoadHTMLGlob("templates/*")
r.Run()
}
func substr(s string, start, length int) string {
runes := []rune(s)
if start >= len(runes) {
return ""
}
end := start + length
if end > len(runes) {
end = len(runes)
}
return string(runes[start:end])
}
func truncate(s string, maxLen int) string {
runes := []rune(s)
if len(runes) <= maxLen {
return s
}
return string(runes[:maxLen]) + "..."
}
func replace(s, old, new string) string {
return strings.ReplaceAll(s, old, new)
}
模板使用:
html
<p>大写: {{ .name | upper }}</p>
<p>截取: {{ substr .text 0 10 }}</p>
<p>截断: {{ .content | truncate 50 }}</p>
<p>替换: {{ replace .text "old" "new" }}</p>
4.2 日期格式化 #
go
func main() {
r := gin.Default()
r.SetFuncMap(template.FuncMap{
"formatDate": formatDate,
"formatTime": formatTime,
"relativeTime": relativeTime,
})
r.LoadHTMLGlob("templates/*")
r.Run()
}
func formatDate(t time.Time, layout string) string {
return t.Format(layout)
}
func formatTime(t time.Time) string {
return t.Format("2006-01-02 15:04:05")
}
func relativeTime(t time.Time) string {
duration := time.Since(t)
switch {
case duration.Seconds() < 60:
return "刚刚"
case duration.Minutes() < 60:
return fmt.Sprintf("%.0f分钟前", duration.Minutes())
case duration.Hours() < 24:
return fmt.Sprintf("%.0f小时前", duration.Hours())
case duration.Hours() < 24*30:
return fmt.Sprintf("%.0f天前", duration.Hours()/24)
default:
return t.Format("2006-01-02")
}
}
模板使用:
html
<p>日期: {{ formatDate .date "2006-01-02" }}</p>
<p>时间: {{ formatTime .date }}</p>
<p>相对时间: {{ relativeTime .createdAt }}</p>
4.3 数字格式化 #
go
func main() {
r := gin.Default()
r.SetFuncMap(template.FuncMap{
"formatNumber": formatNumber,
"formatMoney": formatMoney,
"formatPercent": formatPercent,
})
r.LoadHTMLGlob("templates/*")
r.Run()
}
func formatNumber(n int64) string {
return humanize.Comma(n)
}
func formatMoney(n float64) string {
return fmt.Sprintf("%.2f", n)
}
func formatPercent(n float64) string {
return fmt.Sprintf("%.1f%%", n*100)
}
模板使用:
html
<p>数字: {{ formatNumber .count }}</p>
<p>金额: ¥{{ formatMoney .price }}</p>
<p>百分比: {{ formatPercent .rate }}</p>
4.4 条件函数 #
go
func main() {
r := gin.Default()
r.SetFuncMap(template.FuncMap{
"default": defaultValue,
"empty": isEmpty,
"notEmpty": notEmpty,
"ternary": ternary,
})
r.LoadHTMLGlob("templates/*")
r.Run()
}
func defaultValue(defaultVal, val interface{}) interface{} {
if val == nil || val == "" || val == 0 {
return defaultVal
}
return val
}
func isEmpty(val interface{}) bool {
if val == nil {
return true
}
v := reflect.ValueOf(val)
switch v.Kind() {
case reflect.String:
return v.String() == ""
case reflect.Slice, reflect.Map:
return v.Len() == 0
default:
return false
}
}
func notEmpty(val interface{}) bool {
return !isEmpty(val)
}
func ternary(condition bool, trueVal, falseVal interface{}) interface{} {
if condition {
return trueVal
}
return falseVal
}
模板使用:
html
<p>默认值: {{ default "未设置" .nickname }}</p>
<p>是否为空: {{ if empty .items }}暂无数据{{ end }}</p>
<p>三元运算: {{ ternary .isActive "激活" "未激活" }}</p>
4.5 安全输出 #
go
func main() {
r := gin.Default()
r.SetFuncMap(template.FuncMap{
"safeHTML": safeHTML,
"safeJS": safeJS,
"safeURL": safeURL,
})
r.LoadHTMLGlob("templates/*")
r.Run()
}
func safeHTML(s string) template.HTML {
return template.HTML(s)
}
func safeJS(s string) template.JS {
return template.JS(s)
}
func safeURL(s string) template.URL {
return template.URL(s)
}
模板使用:
html
<!-- 输出原始HTML -->
{{ .content | safeHTML }}
<!-- 输出安全JS -->
<script>
var config = {{ .config | safeJS }};
</script>
<!-- 输出安全URL -->
<img src="{{ .imageUrl | safeURL }}">
4.6 列表操作 #
go
func main() {
r := gin.Default()
r.SetFuncMap(template.FuncMap{
"first": first,
"last": last,
"slice": sliceList,
"contains": contains,
"join": joinStrings,
})
r.LoadHTMLGlob("templates/*")
r.Run()
}
func first(items []interface{}) interface{} {
if len(items) == 0 {
return nil
}
return items[0]
}
func last(items []interface{}) interface{} {
if len(items) == 0 {
return nil
}
return items[len(items)-1]
}
func sliceList(items []interface{}, start, end int) []interface{} {
if start >= len(items) {
return nil
}
if end > len(items) {
end = len(items)
}
return items[start:end]
}
func contains(items []string, item string) bool {
for _, i := range items {
if i == item {
return true
}
}
return false
}
func joinStrings(items []string, sep string) string {
return strings.Join(items, sep)
}
五、函数组合 #
5.1 管道组合 #
html
<!-- 多个函数组合 -->
{{ .name | lower | truncate 10 }}
<!-- 带参数的函数 -->
{{ formatDate .date "2006-01-02" }}
5.2 函数库 #
go
func initFuncMap() template.FuncMap {
return template.FuncMap{
// 字符串
"upper": strings.ToUpper,
"lower": strings.ToLower,
"trim": strings.TrimSpace,
"truncate": truncate,
// 日期
"formatDate": formatDate,
"relativeTime": relativeTime,
// 数字
"formatNumber": formatNumber,
"formatMoney": formatMoney,
// 条件
"default": defaultValue,
"ternary": ternary,
// 安全
"safeHTML": safeHTML,
"safeJS": safeJS,
// 列表
"first": first,
"last": last,
"join": joinStrings,
}
}
func main() {
r := gin.Default()
r.SetFuncMap(initFuncMap())
r.LoadHTMLGlob("templates/*")
r.Run()
}
六、第三方函数库 #
6.1 使用sprig #
bash
go get github.com/Masterminds/sprig/v3
go
import "github.com/Masterminds/sprig/v3"
func main() {
r := gin.Default()
// 使用sprig函数库
r.SetFuncMap(sprig.FuncMap())
r.LoadHTMLGlob("templates/*")
r.Run()
}
sprig常用函数:
html
<!-- 字符串 -->
{{ .name | upper }}
{{ .name | lower }}
{{ .name | title }}
{{ .name | trim }}
{{ .name | trunc 10 }}
{{ .name | replace "old" "new" }}
<!-- 日期 -->
{{ .date | date "2006-01-02" }}
{{ .date | dateModify "-1d" }}
{{ now | date "2006-01-02" }}
<!-- 数字 -->
{{ .number | int }}
{{ .number | float64 }}
{{ .number | add 1 }}
{{ .number | sub 1 }}
{{ .number | mul 2 }}
{{ .number | div 2 }}
<!-- 列表 -->
{{ .list | first }}
{{ .list | last }}
{{ .list | uniq }}
{{ .list | join ", " }}
<!-- 条件 -->
{{ .name | default "unknown" }}
{{ .name | empty }}
{{ ternary .condition "yes" "no" }}
<!-- 加密 -->
{{ .password | sha256sum }}
{{ .password | md5 }}
七、总结 #
7.1 核心要点 #
| 要点 | 说明 |
|---|---|
| 注册函数 | SetFuncMap |
| 内置函数 | len、printf、eq等 |
| 自定义函数 | 定义后注册 |
| 管道操作 | 函数链式调用 |
7.2 最佳实践 #
| 实践 | 说明 |
|---|---|
| 函数库 | 统一管理模板函数 |
| 命名规范 | 使用驼峰命名 |
| 安全输出 | 使用safeHTML等 |
| 复用 | 使用sprig等库 |
7.3 下一步 #
现在你已经掌握了模板函数,接下来让我们学习 静态文件,了解静态资源服务!
最后更新:2026-03-28