Stories

Detail Return Return

gotemplate:高性能go文本模版引擎 - Stories Detail

代碼倉庫 https://github.com/tylitianrui/gotemplate

1.簡述

1.1 高性能

​gotemplate 模版引擎是go語言中、性能最高的文本渲染引擎之一。性能是官方text/template5倍左右,是fasttemplate的1.7~2倍左右。

1.2 自定義佔位符tag

​gotemplate 模版引擎允許用户設置自定義的佔位符tag,例如: [[xxx]]{{.xxx}}[[(xxx}}::@xxx$$% 等。匹配到佔位符的文本,則就會被渲染。如果沒有設置佔位符tag,默認使用{{xxx}}注:xxx 為被替換的參數

模版 設置的佔位符tag 實參 (k-v) 渲染結果
i am [[(name}} [[(}} name:tyltr i am tyltr
i am {{.name}} {{.}} name:tyltr i am tyltr
i am ::@name$$% ::@$$% name:tyltr i am tyltr

1.3 渲染模式

​gotemplate 模版引擎有兩種渲染模式:嚴格模式非嚴格模式

  • 嚴格模式:渲染文本中的每個佔位符。如果要求的參數缺少,就會報錯
  • 非嚴格模式:盡最大努力渲染文本。如果參數缺少,則不渲染或者自動填充默認值

2.基本使用

2.1 非嚴格模式,不設置自動填充

package main

import (
    "fmt"

    "github.com/tylitianrui/gotemplate"
)

func main() {
    tpl := "https://{{demain}}.com?name={{name}}&age={{age}}&birth={{birth}}"

    // Create a new template instance with default tag pair `{{` &` }}` and pre-allocated memory of 1024 bytes.
    t, err := gotemplate.NewTemplate(tpl,
        gotemplate.WithPreAllocateMemory(1024), // Pre-allocate memory for better performance ,1024 bytes
    )

    if err != nil {
        panic(err)
    }

    // Substitution map.
    // "birth" tag is missing in the map
    args := map[string]string{
        "demain": "user.google",
        "bar":    "foobar",
        "name":   "tyltr",
        "age":    "18",
        // "birth" is missing.
    }

    // Non-strict mode, placeholders without corresponding entries in args will remain unchanged in the output.
    s, err := t.ExecString(args, false)
    fmt.Println("err:", err)
    fmt.Println("template:", s)

    // Output:
    // err: <nil>
    // template: https://user.google.com?name=tyltr&age=18&birth={{birth}}
}

説明:

  • 本例子中沒有設置自定義的佔位符tag,則使用默認的 {{}} 匹配
  • t.ExecString(args, false) 第二個參數定義為strict bool

    • false 則是非嚴格模式
    • true 則為嚴格模式

2.2 非嚴格模式,自動填充

package main

import (
    "fmt"

    "github.com/tylitianrui/gotemplate"
)

func main() {
    tpl := "https://[[demain]].com?name=[[name]]&age=[[age]]&birth=[[birth]]"
    t, err := gotemplate.NewTemplate(tpl,
        gotemplate.WithTagPair("[[", "]]"),     // set custom tag pair `[[` & `]]`
        gotemplate.WithPreAllocateMemory(1024), // Pre-allocate memory for better performance ,1024 bytes
        gotemplate.WithAutoFill(""),            // Auto fill missing parameters with empty string
    )
    if err != nil {
        panic(err)
    }

    // Substitution map.
    // "birth" tag is missing in the map
    args := map[string]string{
        "demain": "user.google",
        "bar":    "foobar",
        "name":   "tyltr",
        "age":    "18",
        // "birth" is missing.
    }

    // Non-strict mode, placeholders without corresponding entries in args will remain unchanged in the output.
    s, err := t.ExecString(args, false)
    fmt.Println("err:", err)
    fmt.Println("template:", s)

    // Output:
    // err: <nil>
    // template: https://user.google.com?name=tyltr&age=18&birth=
}

説明:

  • gotemplate.WithAutoFill("") 設置自動填充字段,本例子設置為空字符串填充

2.3 嚴格模式

package main

import (
    "fmt"

    "github.com/tylitianrui/gotemplate"
)

func main() {
    tpl := "https://{{demain}}.com?name={{name}}&age={{age}}&birth={{birth}}"
    t, err := gotemplate.NewTemplate(tpl,
        gotemplate.WithTagPair("{{", "}}"),
        gotemplate.WithPreAllocateMemory(1024),
    )
    if err != nil {
        panic(err)
    }

    // Substitution map.
    // "birth" tag is missing in the map
    args := map[string]string{
        "demain": "user.google",
        "bar":    "foobar",
        "name":   "tyltr",
        "age":    "18",
        // "birth" is missing.
    }

    // strict mode, it returns an error if any placeholder in the template does not have a corresponding entry in args.
    s, err := t.ExecString(args, true)
    fmt.Println("err:", err)
    fmt.Println("template:", s)

    // Output:
    // err: missing parameter
    // template:
}

​説明:

  • 參數birth 缺少,則報錯missing parameter

3.高級用法

高級用法允許用户動態的調整渲染的字段

package main

import (
    "bytes"
    "fmt"
    "io"

    "github.com/tylitianrui/gotemplate"
)

func main() {
    tpl := "https://{{demain}}.com?name={{name}}&age={{age}}&birth={{birth}}"
    t, err := gotemplate.NewTemplate(tpl)
    if err != nil {
        panic(err)
    }
    var buf bytes.Buffer
    err = t.ExecuteFunc(&buf, func(w io.Writer, key string) (int, error) {
        switch key {
        case "demain":
            return w.Write([]byte("user.google"))
        case "name":
            return w.Write([]byte("tyltr"))
        case "age":
            return w.Write([]byte("18"))

        default:
            return w.Write([]byte("<null>")) //  Auto fill missing parameters with "<null>"
        }
    })
    fmt.Println("err:", err)
    fmt.Println("template:", buf.String())

    // Output:
    //err: <nil>
    //template: https://user.google.com?name=tyltr&age=18&birth=<null>

}

4.匹配優先級

一般配置模版都是{{}}[[]]等成對出現的。但對於特殊情況,例如 {{{name{{}}age}} 怎麼渲染呢?這些"噁心"的模板遵守以下規範

4.1佔位符為空,被當作普通文本

案例 模版 標籤 參數(map類型) 效果
案例1 hello,i am {{name}} {{}} name:tyltr hello,i am tyltr
案例2 hello,i am {{}} {{name}} {{}} name:tyltr hello,i am {{}} tyltr

説明:案例2中包含空佔位符 {{}} 會當作普通文本展示。

4.2 非貪婪模式匹配佔位符

案例 模版 標籤 佔位符 參數(map類型) 效果
案例3 hello,i am {{{name}} {{}} name 而不是{name name:tyltr hello,i am {tyltr

説明:案例3 匹配採用非貪婪模式,會匹配儘可能少的字符。所以匹配出的佔位符為 name 而不是{name

4.3 最左側匹配原則

案例 模版 標籤 佔位符 參數(map類型) 效果
案例4 hello,i am {{{name{{}}tyltr}} {{}} name{{ 而不是}}tyltr name{{:ty hello,i am {tytyltr

説明:

  • {{{name{{}}tyltr}} 中間佔位符為空,所以 {{}} 被當做普通文本處理
  • 根據最左側匹配原則 左側 {{{name{{}} 擁有比右側 {{}}tyltr}} 更高優先級
  • 根據非貪婪原則,左側部分會如此匹配 {{{name{{}}

Add a new Comments

Some HTML is okay.