<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          template源碼分析

          共 8568字,需瀏覽 18分鐘

           ·

          2021-05-15 13:33

          golang 的模版使用分兩步


          1,模版解析

          tpl, err := template.Parse(filename)得到文件名為名字的模板,并保存在tpl變量中

          template.ParseFiles(filenames)可以解析一組模板

          template.ParseGlob(pattern)會(huì)根據(jù)pattern解析所有匹配的模板并保存

          2,數(shù)據(jù)綁定

          tpl.Execute(io.Writer, data)去執(zhí)行, 模板渲染后的內(nèi)容寫入到io.Writer中。Data是傳給模板的動(dòng)態(tài)數(shù)據(jù)。

          tpl.ExecuteTemplate(io.Writer, name, data)和上面的簡(jiǎn)單模板類似,只不過(guò)傳入了一個(gè)模板的名字,指定要渲染的模板(因?yàn)閠pl可以包含多個(gè)模板


          嵌套模板定義


          嵌套模板定義如下:

          { {define "footer"}}<footer>   <p>Here is the footer</p></footer>{ {end}}


          其他模板中使用:

          { {template "footer"}}

          模版的數(shù)據(jù)綁定也有兩種方式

          1,模版變量

          通過(guò).獲取變量

          通過(guò){ { .FieldName }}來(lái)訪問它的字段

          鏈?zhǔn)皆L問 { { .Struct.StructTwo.Field }}

          2,函數(shù)變量

          2.1,調(diào)用結(jié)構(gòu)體的方法

          { {if .User.HasPermission "feature-a"}}

          2.2 調(diào)用結(jié)構(gòu)體的函數(shù)變量(屬性是一個(gè)函數(shù)

          type User struct {    ID            int  Email         string  HasPermission func(string) bool}
          { {if (call .User.HasPermission "feature-b")}}

          2.3使用template.FuncMap創(chuàng)建自定義的函數(shù)

          FuncMap通過(guò)map[string]interface{}將函數(shù)名映射到函數(shù)上。注意映射的函數(shù)必須只有一個(gè)返回值,或者有兩個(gè)返回值但是第二個(gè)是error類型。

          testTemplate, err = template.New("hello.gohtml").Funcs(template.FuncMap{    "hasPermission": func(user User, feature string) bool {      if user.ID == 1 && feature == "feature-a" {        return true      }      return false    },  }).ParseFiles("hello.gohtml")
          { { if hasPermission .User "feature-a" }}

          2.4第三方自定義函數(shù)

          比如sprig庫(kù)


          由于函數(shù)變量的方式綁定數(shù)據(jù)可以使用閉包,所以數(shù)據(jù)的綁定時(shí)機(jī)有兩個(gè)

          1,使用閉包

          template.New("hello.gohtml").Funcs(template.FuncMap

          2,Execute數(shù)據(jù)綁定

          tpl.Execute(io.Writer, data)


          常用的兩個(gè)模版包

          "text/template"


          template包實(shí)現(xiàn)了數(shù)據(jù)驅(qū)動(dòng)的用于生成文本輸出的模板。其實(shí)簡(jiǎn)單來(lái)說(shuō)就是將一組文本嵌入另一組文本模版中,返回一個(gè)你期望的文本

          "html/template"

          生成HTML格式的輸出,該包提供了相同的接口,但會(huì)自動(dòng)將輸出轉(zhuǎn)化為安全的HTML格式輸出,可以抵抗一些網(wǎng)絡(luò)攻擊。  內(nèi)部其實(shí)是用了"text/template"


          看下這兩個(gè)包的源碼目錄

          text/template

          cd /usr/local/go/src/text/template 
          tree.|____option.go|____examplefunc_test.go|____testdata| |____file1.tmpl| |____tmpl1.tmpl| |____tmpl2.tmpl| |____file2.tmpl|____examplefiles_test.go|____example_test.go|____exec_test.go|____link_test.go|____multi_test.go|____parse| |____lex_test.go| |____lex.go| |____parse_test.go| |____node.go| |____parse.go|____exec.go|____template.go|____helper.go|____doc.go|____funcs.go

          html/template

          cd /usr/local/go/src/html/template  tree.|____testdata| |____file1.tmpl| |____tmpl1.tmpl| |____fs.zip| |____tmpl2.tmpl| |____file2.tmpl|____clone_test.go|____error.go|____examplefiles_test.go|____example_test.go|____content_test.go|____escape.go|____exec_test.go|____transition_test.go|____multi_test.go|____js_test.go|____element_string.go|____urlpart_string.go|____transition.go|____css_test.go|____template_test.go|____html.go|____state_string.go|____js.go|____html_test.go|____delim_string.go|____template.go|____doc.go|____context.go|____attr_string.go|____content.go|____css.go|____url.go|____escape_test.go|____attr.go|____url_test.go|____jsctx_string.go

          下面簡(jiǎn)單介紹下text/template

          它的工作流程大概分為下面幾步,

          通過(guò)lex 進(jìn)行詞法分析=>parse解析語(yǔ)法樹=>execute渲染生成目標(biāo)文件

          首先看Template結(jié)構(gòu):

          type Template struct {        name string        *parse.Tree        *common        leftDelim  string        rightDelim string}

          name是這個(gè)Template的名稱,Tree是解析樹,common是另一個(gè)結(jié)構(gòu),leftDelim和rightDelim是左右兩邊的分隔符,默認(rèn)為{{和}}。

          type common struct {        tmpl   map[string]*Template // Map from name to defined templates.        option option        muFuncs    sync.RWMutex // protects parseFuncs and execFuncs        parseFuncs FuncMap        execFuncs  map[string]reflect.Value}

          這個(gè)結(jié)構(gòu)的第一個(gè)字段tmpl是一個(gè)Template的map結(jié)構(gòu),key為template的name,value為Template。也就是說(shuō),一個(gè)common結(jié)構(gòu)

          中可以包含多個(gè)Template,而Template結(jié)構(gòu)中又指向了一個(gè)common結(jié)構(gòu)。所以,common是一個(gè)模板組,在這個(gè)模板組中的(tmpl字段)所有Template都共享一個(gè)common(模板組),模板組中包含parseFuncs和execFuncs。

          使用template.New()函數(shù)可以創(chuàng)建一個(gè)空的、無(wú)解析數(shù)據(jù)的模板,同時(shí)還會(huì)創(chuàng)建一個(gè)common,也就是模板組。

          func New(name string) *Template {        t := &Template{                name: name,        }        t.init()        return t}

          其中t為模板的關(guān)聯(lián)名稱,name為模板的名稱,t.init()表示如果模板對(duì)象t還沒有common結(jié)構(gòu),就構(gòu)造一個(gè)新的common組:

          func (t *Template) init() {        if t.common == nil {                c := new(common)                c.tmpl = make(map[string]*Template)                c.parseFuncs = make(FuncMap)                c.execFuncs = make(map[string]reflect.Value)                t.common = c        }}

          新創(chuàng)建的common是空的,只有進(jìn)行模板解析(Parse(),ParseFiles()等操作)之后,才會(huì)將模板添加到common的tmpl字段(map結(jié)構(gòu))中。

          在執(zhí)行了Parse()方法后,將會(huì)把模板name添加到common tmpl字段的map結(jié)構(gòu)中,其中模板name為map的key,模板為map的value。

          除了template.New()函數(shù),還有一個(gè)Template.New()方法:

          func (t *Template) New(name string) *Template {        t.init()        nt := &Template{                name:       name,                common:     t.common,                leftDelim:  t.leftDelim,                rightDelim: t.rightDelim,        }        return nt}

          通過(guò)調(diào)用t.New()方法,可以創(chuàng)建一個(gè)新的名為name的模板對(duì)象,并將此對(duì)象加入到t模板組中。

          Execute()和ExecuteTemplate()

          這兩個(gè)方法都可以用來(lái)應(yīng)用已經(jīng)解析好的模板,應(yīng)用表示對(duì)需要評(píng)估的數(shù)據(jù)進(jìn)行操作,并和無(wú)需評(píng)估數(shù)據(jù)進(jìn)行合并,然后輸出>到io.Writer中:

          func (t *Template) Execute(wr io.Writer, data interface{}) error

          func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error

          兩者的區(qū)別在于Execute()是應(yīng)用整個(gè)common中已定義的模板對(duì)象,而ExecuteTemplate()可以選擇common中某個(gè)已定義的模板進(jìn)行應(yīng)用。


          FuncMap和Funcs()

          template內(nèi)置了一系列函數(shù),但這些函數(shù)畢竟有限,可能無(wú)法滿足特殊的需求。template允許我們定義自己的函數(shù),添加到common中,然后就可以在待解析的內(nèi)容中像使用內(nèi)置函數(shù)一樣使用自定義的函數(shù)。

          下面看看模版解析的 過(guò)程

          parse.Parse 函數(shù)把文本解析成map[string]*parse.Tree的樹map對(duì)象,然后把它append到當(dāng)前模板的t.temp中

          func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {        defer t.recover(&err)        t.ParseName = t.Name        t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim), treeSet)        t.text = text        t.parse()        t.add()        t.stopParse()        return t, nil}
          // lex creates a new scanner for the input string.func lex(name, input, left, right string) *lexer {        if left == "" {                left = leftDelim        }        if right == "" {                right = rightDelim        }        l := &lexer{                name:       name,                input:      input,                leftDelim:  left,                rightDelim: right,                items:      make(chan item),                line:       1,        }        go l.run()        return l}
          // run runs the state machine for the lexer.func (l *lexer) run() {        for state := lexText; state != nil; {                state = state(l)        }        close(l.items)}
          // lexText scans until an opening action delimiter, "{{".func lexText(l *lexer) stateFn {        l.width = 0        if x := strings.Index(l.input[l.pos:], l.leftDelim); x >= 0 {                ldn := Pos(len(l.leftDelim))                l.pos += Pos(x)                trimLength := Pos(0)                if strings.HasPrefix(l.input[l.pos+ldn:], leftTrimMarker) {                        trimLength = rightTrimLength(l.input[l.start:l.pos])                }                l.pos -= trimLength                if l.pos > l.start {                        l.emit(itemText)                }                l.pos += trimLength                l.ignore()                return lexLeftDelim        } else {                l.pos = Pos(len(l.input))        }        // Correctly reached EOF.        if l.pos > l.start {                l.emit(itemText)        }        l.emit(itemEOF)        return nil}

          下面看下數(shù)據(jù)綁定的過(guò)程,其實(shí)就是遍歷語(yǔ)法樹

          // Walk functions step through the major pieces of the template structure,// generating output as they go.func (s *state) walk(dot reflect.Value, node parse.Node) {        s.at(node)        switch node := node.(type) {        case *parse.ActionNode:                // Do not pop variables so they persist until next end.                // Also, if the action declares variables, don't print the result.                val := s.evalPipeline(dot, node.Pipe)                if len(node.Pipe.Decl) == 0 {                        s.printValue(node, val)                }        case *parse.IfNode:                s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)        case *parse.ListNode:                for _, node := range node.Nodes {                        s.walk(dot, node)                }        case *parse.RangeNode:                s.walkRange(dot, node)        case *parse.TemplateNode:                s.walkTemplate(dot, node)        case *parse.TextNode:                if _, err := s.wr.Write(node.Text); err != nil {                        s.writeError(err)                }        case *parse.WithNode:                s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)        default:                s.errorf("unknown node: %s", node)        }}



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 50
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  蜜桃在线码无精品秘 入口九色 | 成人欧美一区二区三区男男 | 国产精品久久久久久久久午夜福利 | 黄色国产精品 | 精品秘 一区二三区免费 |