技术控

    今日:59| 主题:49312
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] Go 模板嵌套最佳实践

[复制链接]
秀下限 发表于 2016-10-9 17:05:37
99 0

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
Go 官方库提供了两个模板库:    text/template和    html/template。这两个库类似,只不过    html/template对html格式做了特别的处理,当需要输出html格式的代码时需要使用    html/template。  
  使用模版,可以帮助我们写一些通用的代码,或者提供清晰的文件布局, 或者提供一个代码生成器。
  官方文档提供了很好的模版的使用方法, 其中    text/template提供了基础的模版的使用方法,比如 Action、 Argument、Pipeline、Variable、Function、模版嵌套的介绍,    html/template对 Context 进行了介绍。 本文假定你已经了解了这些基础知识。如果你还不清楚,或者还没有用过模版,可以参考文末的参考文档进行学习。  
  虽然    text/template官方文档对模版嵌套简单了介绍,但是对于如何使用嵌套模版进行实际开发,以及注意事项并没有详细的介绍,所以本文着重介绍嵌套模版的使用。  
  虽然本文名称为嵌套模版的最佳实践,但是准确的讲,这只是最佳实践之一。如果读者有其它的实践方案,或者更好的处理模版嵌套的方案,欢迎讨论。
  在我们开发web应用程序的时候,不可避免的要使用到模版。
  一般一个网站包含很多的页面,比如新闻页面、注册页面、文章列表等。 不同的新闻可能使用同一个新闻布局模版,不同的文章详细页也可能使用同一个文章布局页面, 同时新闻布局模版和文章布局页面也可能有一些公用的东西,比如header、footer、导航栏等。如何将这些公用的东西抽取成统一的模版呢?
  本文将逐步介绍这些技术, 首先介绍嵌套文件的使用。
  Parse vs ParseFiles vs ParseBlob

  首先我们看一下这三个文件有何不同。 事实上是五个文件:
  1. funcParseFiles(filenames ...string) (*Template, error)
  2. funcParseGlob(patternstring) (*Template, error)
  3. func(t *Template) Parse(textstring) (*Template, error)
  4. func(t *Template) ParseFiles(filenames ...string) (*Template, error)
  5. func(t *Template) ParseGlob(patternstring) (*Template, error)
复制代码
     Parse用来解析一个字符串,字符串代表模版的内容,并且嵌套的模版也会和这个模版进行关联。  
  1. tmpl, err := template.New("name").Parse(...)
  2. // Error checking elided
  3. err = tmpl.Execute(out, data)
复制代码

  1. import"html/template"
  2. ...
  3. t, err := template.New("foo").Parse(`\{\{define "T"\}\}Hello, \{\{.\}\}!\{\{end\}\}`)
  4. err = t.ExecuteTemplate(out, "T","<script>alert('you have been pwned')</script>")
复制代码
         ParseFiles用来解析一组命名的文件模版,当模版定义在不同的文件中的时候,使用这个方法可以产生一个可执行的模版,模版执行的时候不会出错。   
          ParseGlob与      ParseFiles类似,它使用      filepath.Glob模式匹配的方式遍历文件,将这些文件生成模版。   
      这两个函数还可以作为      *Template的方法使用。作为函数使用的时候,它返回模版的名字是第一个文件的名字,模版以第一个文件作为base模版。   
    同时后面的文件也会生成模版作为这个模版的关联模板,你可以通过      Lookup方法查找到这个模版,因为每个模版都保存着它的关联模版:   
   
  1. set map[string]*Template
复制代码
我们以一个例子演示一下嵌套模版的使用。
    当前文件夹下有两个文件。
    header.html   
   
  1. Title is \{\{.Title\}\}
  2. \{\{template "footer"\}\}
复制代码
footer.html

  1. \{\{define "footer"\}\}Body is \{\{.Body\}\}\{\{end\}\}
复制代码
测试程序:
  1. packagemain
  2. import(
  3. "fmt"
  4. "html/template"
  5. "net/http"
  6. )
  7. funchandler(w http.ResponseWriter, r *http.Request) {
  8. t, _ := template.ParseFiles("header.html","footer.html")
  9. err := t.Execute(w, map[string]string{"Title":"My title","Body":"Hi this is my body"})
  10. iferr !=nil{
  11. panic(err)
  12. }
  13. }
  14. funcmain() {
  15. http.HandleFunc("/", handler)
  16. http.ListenAndServe(":8080",nil)
  17. }
复制代码
浏览器访问    http://localhost:8080/会得到渲染的结果:  
  1. Title is My title
  2. Body is
复制代码
可以看到结果基本符合预期。    header.html嵌套了    footer模版,渲染的时候以    header.html为主模版显示,嵌套渲染了    footer.html。  
  但是上面的结果显示有一点点问题,就是    footer渲染的时候并没有显示body的结果,这是因为data传给了主模版,嵌套模版如果要使用这个数据,需要在嵌套的地方把data传递给它。我们可以修改    header.html:  
  1. Title is \{\{.Title\}\}
  2. \{\{template "footer" <div class=""></div>\}\}
复制代码
这样    footer.html就可以使用传入的data渲染了:  
  1. Title isMy title
  2. Body isHithisismybody
复制代码
在使用    ParseFiles、    ParseGlob    函数的时候,默认以文件的路径的最后一个部分作为模版名称, 比如文件    a/foo的模版名称为    foo,但是如果参数中不同的文件夹下有相同的文件名时,最后那个同名的模版文件会"覆盖"前面的重名文件模版,官方库的实现中不能保存重名的模版文件。  
  Execute vs ExecuteTemplate

  上面的例子显示了一个简单的嵌套模版的使用,但是如果我们把两个文件的顺序交换一下,如下所示:
  1. tmpl, err := template.New("name").Parse(...)
  2. // Error checking elided
  3. err = tmpl.Execute(out, data)
  4. 0
复制代码
   运行后浏览器访问一下,会发现没报错,但是也没有渲染任何东西。
    这是因为`template.ParseFiles`返回的模版以第一个为主模版。在这种情况下      footer.html没有可渲染的东西。   
    在这种情况下,我们就需要显示的指定要渲染的模版:
  1. tmpl, err := template.New("name").Parse(...)
  2. // Error checking elided
  3. err = tmpl.Execute(out, data)
  4. 1
复制代码
使用    ExecuteTemplate可以选择渲染    t关联的模版作为渲染的主模版。上面的例子中我们选择    header.html作为主模版,所以它可以正常渲染。  
  或者, 通过下面的方法也能正常渲染,它和上面的代码基本是等价的(只有escape细微的差别)。
  1. tmpl, err := template.New("name").Parse(...)
  2. // Error checking elided
  3. err = tmpl.Execute(out, data)
  4. 2
复制代码
所以可以看到    Execute和    ExecuteTemplate的功能的差别,当你想用指定的关联的模版渲染时,就使用    ExecuteTemplate。  
  最佳实践

  有了上面的基础,我们就可以解决文章开始提出的问题: 如何规划一个网站的模版?
  假设有三个文件:    hello.html、    header.html、    footer.html,其中    hello.html是一个栏目的统一布局,    header.html是所有的布局中需要嵌套的html的首部,比如网站描述、css、导航栏等,    footer.html包含全局的javascript文件的引入、版权声明等等。  
    我们创建两个文件夹:      layouts、      widgets,将      hello.html放入到      layouts文件夹中,      header.html、      footer.html放入到      widgets文件夹中,   
    我们就以这三个文件作为例子,看看嵌套模版在网站中的应用。
    但是在进一步介绍之前,我需要介绍上面代码中的一个问题。可以看到,上面的每个请求都会读取模版文件再进行解析,这会严重影响程序的性能,解决方案就是预先读取这些文件并生成Template对象,这样请求来了就直接使用解析好的template对象即可。
  1. tmpl, err := template.New("name").Parse(...)
  2. // Error checking elided
  3. err = tmpl.Execute(out, data)
  4. 3
复制代码
通过一个    map对象,我们保存了每一个布局对应的template对象。  
  然后提供一个模版渲染的方法:
  1. tmpl, err := template.New("name").Parse(...)
  2. // Error checking elided
  3. err = tmpl.Execute(out, data)
  4. 4
复制代码
我在为    rpcx开发web GUI的时候使用了这个方法:    rpcx-ui。  
  当然通过一些变换也可以实现其它的模版设计, 比如一个    base.html模版,里面定义好了header,footer,还嵌套了    body模版,通过不同的body模版实现不同模版布局,比如    news.html定义了显示news的body,    music.html定义了music的body,这也是一个不错的模版布局。  
  参考文档

  
       
  •       https://golang.org/pkg/text/template/   
  •       https://golang.org/pkg/html/template/   
  •       https://elithrar.github.io/article/approximating-html-template-inheritance/   
  •       https://groups.google.com/forum/#!topic/golang-nuts/EweRbwa_tks   
  •       http://stackoverflow.com/questions/12224436/multiple-files-using-template-parsefiles-in-golang   
  •       https://gohugo.io/templates/go-templates/   
  •       https://astaxie.gitbooks.io/build-web-application-with-golang/content/en/07.4.html  
友荐云推荐




上一篇:江湖外卖O2O系统同城配送解决方案任意场景由您决定!
下一篇:iOS原生二维码的生成与扫描
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表