doubiaode0460 2014-06-07 05:13
浏览 32
已采纳

优化HTML /模板组成

I'm looking to see if there is a better (faster, more organised) way to split up my templates in Go. I strongly prefer to stick to html/template (or a wrapper thereof) since I trust its security model.

  • Right now I use template.ParseGlob to parse all of my template files in within init().
  • I apply template.Funcs to the resulting templates
  • I set a $title in each template (i.e. listing_payment.tmpl) and pass this to the content template.
  • I understand that html/template caches templates in memory once parsed
  • My handlers only call t.ExecuteTemplate(w, "name.tmpl", map[string]interface{}) and don't do any silly parsing on each request.
  • I compose templates from multiple pieces (and this is the bit I find clunky) as below:

    {{ $title := "Page Title" }}
    {{ template "head" $title }}
    {{ template "checkout" }}
    {{ template "top" }}
    {{ template "sidebar_details" . }}
    {{ template "sidebar_payments" }}
    {{ template "sidebar_bottom" }}
    
    <div class="bordered-content">
      ...
          {{ template "listing_content" . }}
      ...
    </div>
    
    {{ template "footer"}}
    {{ template "bottom" }}
    

My three questions are:

  1. Is this performant, or do the multiple {{ template "name" }} tags result in a potential per-request performance hit? I see a lot of write - broken pipe errors when stress testing heavier pages. This might just be due to socket timeouts (i.e. socket closing before the writer can finish) rather than some kind of per-request composition, though (correct me if otherwise!)

  2. Is there a better way to do this within the constraints of the html/template package? The first example in Django's template docs approaches what I'd like. Extend a base layout and replace the title, sidebar and content blocks as needed.

  3. Somewhat tangential: when template.ExecuteTemplate returns an error during a request, is there an idiomatic way to handle it? If I pass the writer to an error handler I end up with soup on the page (because it just continues writing), but a re-direct doesn't seem like idiomatic HTTP.

  • 写回答

1条回答 默认 最新

  • dongzhang6021 2014-06-09 12:21
    关注

    With some help on Reddit I managed to work out a fairly sensible (and performant) approach to this that allows:

    • Building layouts with content blocks
    • Creating templates that effectively "extend" these layouts
    • Filling in blocks (scripts, sidebars, etc.) with other templates

    base.tmpl

    <html>
    <head>
        {{ template "title" .}}
    </head>
    <body>
        {{ template "scripts" . }}
        {{ template "sidebar" . }}
        {{ template "content" . }}
    <footer>
        ...
    </footer>
    </body>
    

    index.tmpl

    {{ define "title"}}<title>Index Page</title>{{ end }}
    // We must define every block in the base layout.
    {{ define "scripts" }} {{ end }} 
    {{ define "sidebar" }}
        // We have a two part sidebar that changes depending on the page
        {{ template "sidebar_index" }} 
        {{ template "sidebar_base" }}
    {{ end }}
    {{ define "content" }}
        {{ template "listings_table" . }}
    {{ end }}
    

    ... and our Go code, which leverages the map[string]*template.Template approach outlined in this SO answer:

    var templates map[string]*template.Template
    
    var ErrTemplateDoesNotExist = errors.New("The template does not exist.")
    
    // Load templates on program initialisation
    func init() {
        if templates == nil {
            templates = make(map[string]*template.Template)
        }
    
        templates["index.html"] = template.Must(template.ParseFiles("index.tmpl", "sidebar_index.tmpl", "sidebar_base.tmpl", "listings_table.tmpl", "base.tmpl"))
        ...
    }
    
    // renderTemplate is a wrapper around template.ExecuteTemplate.
    func renderTemplate(w http.ResponseWriter, name string, data map[string]interface{}) error {
        // Ensure the template exists in the map.
        tmpl, ok := templates[name]
        if !ok {
            return ErrTemplateDoesNotExist
        }
    
        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        tmpl.ExecuteTemplate(w, "base", data)
    
        return nil
    }
    

    From initial benchmarks (using wrk) it seems to be a fair bit more performant when it comes to heavy load, likely due to the fact that we're not passing around a whole ParseGlob worth of templates every request. It also makes authoring the templates themselves a lot simpler.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 R语言Rstudio突然无法启动
  • ¥15 关于#matlab#的问题:提取2个图像的变量作为另外一个图像像元的移动量,计算新的位置创建新的图像并提取第二个图像的变量到新的图像
  • ¥15 改算法,照着压缩包里边,参考其他代码封装的格式 写到main函数里
  • ¥15 用windows做服务的同志有吗
  • ¥60 求一个简单的网页(标签-安全|关键词-上传)
  • ¥35 lstm时间序列共享单车预测,loss值优化,参数优化算法
  • ¥15 Python中的request,如何使用ssr节点,通过代理requests网页。本人在泰国,需要用大陆ip才能玩网页游戏,合法合规。
  • ¥100 为什么这个恒流源电路不能恒流?
  • ¥15 有偿求跨组件数据流路径图
  • ¥15 写一个方法checkPerson,入参实体类Person,出参布尔值