我正在寻找一种更好(更快,更有条理)的方式在Go中拆分我的模板。我非常喜欢坚持使用html / template(或其包装),因为我相信它的安全模型。
template.ParseGlob
来解析init()
内的所有模板文件。$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" }}
我的三个问题是:
这是否具有效果,或多个{{ template "name" }}
标签是否会导致潜在的每次请求性能下降?压力测试较重的页面时,我发现很多write - broken pipe
错误。这可能仅仅是因为套接字超时(即在编写器完成之前关闭套接字)而不是某种按请求组合,但是如果不这样,请更正我!)
在html / template包的约束下有更好的方法吗? Django's template docs中的第一个示例接近我想要的。扩展基本布局并根据需要替换标题,侧边栏和内容块。
有点切向:当template.ExecuteTemplate在请求期间返回错误时,是否有一种惯用的方法来处理它?如果我将编写器传递给错误处理程序,我最终会在页面上使用汤(因为它只是继续编写),但是重定向似乎不是惯用的HTTP。
答案 0 :(得分:8)
在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
)来看,在重负载方面似乎更有效率,可能是因为我们并没有绕过整个{{1}每个请求都有值模板。它还使得创建模板本身变得更加简单。