模板函数 #

一、模板函数概述 #

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