【问题标题】:Optimising html/template Composition优化 html/模板组合
【发布时间】:2014-07-28 10:30:15
【问题描述】:

我正在寻找是否有更好(更快、更有条理)的方式来拆分我在 Go 中的模板。我非常喜欢坚持使用 html/template(或其包装器),因为我信任它的安全模型。

  • 现在我使用template.ParseGlob 来解析init() 中的所有模板文件。
  • 我将 template.Funcs 应用于生成的模板
  • 我在每个模板中设置了一个$title(即listing_payment.tmpl)并将其传递给内容模板。
  • 据我了解,html/template 解析后会将模板缓存在内存中
  • 我的处理程序只调用 t.ExecuteTemplate(w, "name.tmpl", map[string]interface{}) 并且不对每个请求进行任何愚蠢的解析。
  • 我从多个部分组成模板(这是我觉得笨拙的一点),如下所示:

    {{ $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" }}
    

我的三个问题是:

  1. 这是高效的,还是多个{{ template "name" }} 标记会导致潜在的每个请求性能下降?在对较重的页面进行压力测试时,我看到很多 write - broken pipe 错误。这可能只是由于套接字超时(即在编写器完成之前关闭套接字)而不是某种按请求组合,但是(如果不是,请纠正我!)

  2. 在 html/template 包的约束下,有没有更好的方法来做到这一点? Django's template docs 中的第一个示例接近我想要的。扩展基本布局并根据需要替换标题、侧边栏和内容块。

  3. 有点切题:当 template.ExecuteTemplate 在请求期间返回错误时,是否有惯用的方法来处理它?如果我将编写器传递给错误处理程序,我最终会在页面上看到汤(因为它只是继续写入),但重定向似乎不像惯用的 HTTP。

【问题讨论】:

    标签: go


    【解决方案1】:

    在 Reddit 上的一些帮助下,我设法找到了一种相当明智(和高效)的方法,它允许:

    • 使用内容块构建布局
    • 创建有效“扩展”这些布局的模板
    • 使用其他模板填充块(脚本、侧边栏等)

    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 }}
    

    ...以及我们的 Go 代码,它利用了 in this SO answer 概述的 map[string]*template.Template 方法:

    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
    }
    

    从最初的基准测试(使用wrk)来看,当涉及到重负载时,它的性能似乎要好一些,这可能是因为我们没有在每个请求中传递整个ParseGlob 的模板价值.它还使创作模板本身变得更加简单。

    【讨论】:

    • 我是 Go 的超级新手,正在尝试解决这个问题。 data 参数是否只是您发送到模板的参数哈希?
    • @wuliwong 因为数据是interface{},它可以是任何你想要的。我通常会传递一个map[string]interface{},其中映射键是“user”、“itemList”等。
    • 一位同事指出.ExecuteTemplate 接受参数的方式与.Execute 相同。我的问题在其他地方。我将参数传递给嵌套模板。感谢您的回复!
    • 如果您有用户角色并且每个角色都有不同的内容菜单怎么办?为每个角色创建不同的模板[“index.html”]?好的,那么似乎唯一的解决方案是将您的方法与 Web 组件一起使用,以便在不创建新模板的情况下为每个角色加载子内容。
    • @cespinoza 在渲染模板时加载它或使用 Go 1.6 中的新块功能 - tip.golang.org/doc/go1.6#template
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-10
    • 1970-01-01
    • 2012-12-19
    • 2020-07-18
    相关资源
    最近更新 更多