@zwh8800
2016-04-23T20:19:17.000000Z
字数 3763
阅读 330118
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 string
template string
data 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 string
HTML string
JS string
JSStr string
URL string
)
假如在模版中
<img src="{{ .img.url }}">
是无法直接输出的。需要用到上面的几个类型。
data := map[string]interface{} {
url: template.URL(url),
}
其他的类似。
golang起的这个名字有点奇怪,当作是 expression
(表达式)就好了。有三种使用方法
是可以直接调用方法或函数的,不过不需要加小括号。