优化html /模板组合

eli*_*rar 10 go

我想看看是否有一种更好(更快,更有条理)的方式在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" }}
    
    Run Code Online (Sandbox Code Playgroud)

我的三个问题是:

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

  2. 在html/template包的约束下有更好的方法吗?Django的模板文档中的第一个示例接近我想要的.扩展基本布局并根据需要替换标题,侧边栏和内容块.

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

eli*_*rar 8

在Reddit的一些帮助下,我设法制定了一个相当明智(和高效)的方法,允许:

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

base.tmpl

<html>
<head>
    {{ template "title" .}}
</head>
<body>
    {{ template "scripts" . }}
    {{ template "sidebar" . }}
    {{ template "content" . }}
<footer>
    ...
</footer>
</body>
Run Code Online (Sandbox Code Playgroud)

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 }}
Run Code Online (Sandbox Code Playgroud)

......以及我们的Go代码,它利用了本SO答案中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
}
Run Code Online (Sandbox Code Playgroud)

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