@zwh8800
2016-04-23T12:19:17.000000Z
字数 3763
阅读 330814
blog golang template
最近几天用golang写自己的博客系统,手贱选用了golang自己的template包做模版系统。发现这个模版里面好多不符合常识的地方,记录一下。(偷懒没直接用pongo2真是败笔
首先,golang的template分在两个package里,我一开始只看 html/template 包了,一直吐槽为什么文档这么不全,后来发现大部分功能在 text/template 里,html包只负责部分html特有的功能。
先看两种常见的模版布局方式吧(都是伪代码):
<!-- content.html -->{{ include "header.html" }}<div class="content"><!-- content --></div>{{ include "footer.html" }}<!-- end of content.html -->
<!-- header.html --><!DOCTYPE html><html><head><meta charset="utf-8"><title>xxx</title><link href="xxx.css" rel="stylesheet" media="screen"><script src="xxx.js"></script></head><body><!-- end of header.html -->
<!-- footer.html --></body></html><!-- end end of footer.html -->
<!-- layout.html -->{{ define "layout" }}<!DOCTYPE html><html><head><meta charset="utf-8"><title>xxx</title><link href="xxx.css" rel="stylesheet" media="screen"><script src="xxx.js"></script>{{ template "moreStyles" }}</head><body>{{ template "content" }}{{ template "moreScripts" }}</body></html>{{ end }}<!-- end of footer.html -->
{{ use "layout" }}{{ define "content" }}<!-- content -->{{ end }}{{ define "moreScript" }}<script src="you-script.js"></script>{{ end }}
一般我习惯layout方式的模版,这样结构清晰,而且IDE友好。但是golang的template好像没有对layout格式的模版做特别的支持。只好找一种折衷的比较优雅的实现方式。
首先,在模版目录下新建一个 common 目录,用于存放各种公共的template。之后,在下面创建 layout.html 。
{{ define "layout" }}<!DOCTYPE html><html><head><meta charset="utf-8"><title>{{ template "title" .}}</title><link href="common.css" rel="stylesheet">{{ template "moreStyles" .}}</head><body>{{ template "content" .}}<script src="common.js"></script>{{ template "moreScripts" .}}</body></html>{{ end }}
这个文件使用action define 定义了一个叫做 layout 的模版,这个layout模版分别引用了 title、 moreStyles、 content、 moreScripts 这四个模版。
之后,在模版目录下新建真正的view,比如 note.html。
{{/* 定义layout所需要的template */}}{{ define "title" }}{{ .note.Title }} - {{ .site.Name }}{{ end }}{{ define "moreHeaders" }}{{ end }}{{ define "moreStyles" }}<link href="/static/css/note.css" rel="stylesheet">{{ end }}{{ define "moreScripts" }}<script src="/static/js/jquery-2.2.2.min.js"></script><script src="/static/js/note.js"></script><script>// inline script</script>{{ end }}{{ define "content" }}<article class="note">{{ .note.UnescapedContent }}</article>{{ end }}{{/* 引用layout,传入scope */}}{{ template "layout" . }}
这个需要文件定义所有被layout引用的模版项(为空也要定义),然后最重要的一句是 {{ template "layout" . }} 这里把 . 传给了layout,从而能使 layout 访问model。
总之golang的template只使用了两个关键字 template 和 define 完成这个工作,可以算好处也可以算坏处(不够直观)
最后,在代码中这样调用:
const templateDir = "template"const commonDir = "common"type Render struct {templateName stringtemplate stringdata interface{}}func NewRender(template string, data interface{}) *Render {return &Render{template,path.Join(templateDir, template),data,}}func (r *Render) Render(w http.ResponseWriter) error {util.WriteContentType(w, []string{"text/html; charset=utf-8"})t := template.New("")if _, err := t.ParseGlob(path.Join(templateDir,commonDir, "*")); err != nil {return err}if _, err := t.ParseFiles(r.template); err != nil {return err}return t.ExecuteTemplate(w, r.templateName, r.data)}
另外,可以在common里定义更多通用的小组件,以便调用,比如 tag、article 之类的。
golang的escape做的很好,可以区分当前语境(Contexts),把变量放到不同的地方会有不同的输出:
| Context | {{.}} 的输出 |
|---|---|
{{.}} |
O'Reilly: How are <i>you</i>? |
<a title='{{.}}'> |
O'Reilly: How are you? |
<a href="/{{.}}"> |
O'Reilly: How are %3ci%3eyou%3c/i%3e? |
<a href="?q={{.}}"> |
O'Reilly%3a%20How%20are%3ci%3e...%3f |
<a onx='f("{{.}}")'> |
O\x27Reilly: How are \x3ci\x3eyou...? |
<a onx='f({{.}})'> |
"O\x27Reilly: How are \x3ci\x3eyou...?" |
<a onx='pattern = /{{.}}/;'> |
O\x27Reilly: How are \x3ci\x3eyou...\x3f |
假如 {{.}} 是 O'Reilly: How are <i>you</i>?,把 {{.}} 放到不同的地方会有不同的输出。
但是怎么让变量输出它本身的模样呢,html/template 提供了几个type来做这件事:
type (CSS stringHTML stringJS stringJSStr stringURL string)
假如在模版中
<img src="{{ .img.url }}">
是无法直接输出的。需要用到上面的几个类型。
data := map[string]interface{} {url: template.URL(url),}
其他的类似。
golang起的这个名字有点奇怪,当作是 expression (表达式)就好了。有三种使用方法
是可以直接调用方法或函数的,不过不需要加小括号。
