golang template 学习笔记

golang template

2016-04-07 16:24 PM

最近几天用golang写自己的博客系统,手贱选用了golang自己的template包做模版系统。发现这个模版里面好多不符合常识的地方,记录一下。(偷懒没直接用pongo2真是败笔



首先,golang的template分在两个package里,我一开始只看 html/template 包了,一直吐槽为什么文档这么不全,后来发现大部分功能在 text/template 里,html包只负责部分html特有的功能。


Layout

先看两种常见的模版布局方式吧(都是伪代码):

1. include 方式

  1. <!-- content.html -->
  2. {{ include "header.html" }}
  3. <div class="content">
  4. <!-- content -->
  5. </div>
  6. {{ include "footer.html" }}
  7. <!-- end of content.html -->
  1. <!-- header.html -->
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <meta charset="utf-8">
  6. <title>
  7. xxx
  8. </title>
  9. <link href="xxx.css" rel="stylesheet" media="screen">
  10. <script src="xxx.js"></script>
  11. </head>
  12. <body>
  13. <!-- end of header.html -->
  1. <!-- footer.html -->
  2. </body>
  3. </html>
  4. <!-- end end of footer.html -->

2. layout 方式

  1. <!-- layout.html -->
  2. {{ define "layout" }}
  3. <!DOCTYPE html>
  4. <html>
  5. <head>
  6. <meta charset="utf-8">
  7. <title>
  8. xxx
  9. </title>
  10. <link href="xxx.css" rel="stylesheet" media="screen">
  11. <script src="xxx.js"></script>
  12. {{ template "moreStyles" }}
  13. </head>
  14. <body>
  15. {{ template "content" }}
  16. {{ template "moreScripts" }}
  17. </body>
  18. </html>
  19. {{ end }}
  20. <!-- end of footer.html -->
  1. {{ use "layout" }}
  2. {{ define "content" }}
  3. <!-- content -->
  4. {{ end }}
  5. {{ define "moreScript" }}
  6. <script src="you-script.js"></script>
  7. {{ end }}

3. 实现

一般我习惯layout方式的模版,这样结构清晰,而且IDE友好。但是golang的template好像没有对layout格式的模版做特别的支持。只好找一种折衷的比较优雅的实现方式。

首先,在模版目录下新建一个 common 目录,用于存放各种公共的template。之后,在下面创建 layout.html

  1. {{ define "layout" }}
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <meta charset="utf-8">
  6. <title>
  7. {{ template "title" .}}
  8. </title>
  9. <link href="common.css" rel="stylesheet">
  10. {{ template "moreStyles" .}}
  11. </head>
  12. <body>
  13. {{ template "content" .}}
  14. <script src="common.js"></script>
  15. {{ template "moreScripts" .}}
  16. </body>
  17. </html>
  18. {{ end }}

这个文件使用action define 定义了一个叫做 layout 的模版,这个layout模版分别引用了 titlemoreStylescontentmoreScripts 这四个模版。

之后,在模版目录下新建真正的view,比如 note.html

  1. {{/* 定义layout所需要的template */}}
  2. {{ define "title" }}
  3. {{ .note.Title }} - {{ .site.Name }}
  4. {{ end }}
  5. {{ define "moreHeaders" }}
  6. {{ end }}
  7. {{ define "moreStyles" }}
  8. <link href="/static/css/note.css" rel="stylesheet">
  9. {{ end }}
  10. {{ define "moreScripts" }}
  11. <script src="/static/js/jquery-2.2.2.min.js"></script>
  12. <script src="/static/js/note.js"></script>
  13. <script>
  14. // inline script
  15. </script>
  16. {{ end }}
  17. {{ define "content" }}
  18. <article class="note">
  19. {{ .note.UnescapedContent }}
  20. </article>
  21. {{ end }}
  22. {{/* 引用layout,传入scope */}}
  23. {{ template "layout" . }}

这个需要文件定义所有被layout引用的模版项(为空也要定义),然后最重要的一句是 {{ template "layout" . }} 这里把 . 传给了layout,从而能使 layout 访问model。

总之golang的template只使用了两个关键字 templatedefine 完成这个工作,可以算好处也可以算坏处(不够直观)

最后,在代码中这样调用:

  1. const templateDir = "template"
  2. const commonDir = "common"
  3. type Render struct {
  4. templateName string
  5. template string
  6. data interface{}
  7. }
  8. func NewRender(template string, data interface{}) *Render {
  9. return &Render{
  10. template,
  11. path.Join(templateDir, template),
  12. data,
  13. }
  14. }
  15. func (r *Render) Render(w http.ResponseWriter) error {
  16. util.WriteContentType(w, []string{"text/html; charset=utf-8"})
  17. t := template.New("")
  18. if _, err := t.ParseGlob(path.Join(templateDir,
  19. commonDir, "*")); err != nil {
  20. return err
  21. }
  22. if _, err := t.ParseFiles(r.template); err != nil {
  23. return err
  24. }
  25. return t.ExecuteTemplate(w, r.templateName, r.data)
  26. }

另外,可以在common里定义更多通用的小组件,以便调用,比如 tagarticle 之类的。


Unescape

golang的escape做的很好,可以区分当前语境(Contexts),把变量放到不同的地方会有不同的输出:

Context {{.}} 的输出
{{.}} O'Reilly: How are &lt;i&gt;you&lt;/i&gt;?
<a title='{{.}}'> O&#39;Reilly: How are you?
<a href="/{{.}}"> O&#39;Reilly: How are %3ci%3eyou%3c/i%3e?
<a href="?q={{.}}"> O&#39;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来做这件事:

  1. type (
  2. CSS string
  3. HTML string
  4. JS string
  5. JSStr string
  6. URL string
  7. )

假如在模版中

  1. <img src="{{ .img.url }}">

是无法直接输出的。需要用到上面的几个类型。

  1. data := map[string]interface{} {
  2. url: template.URL(url),
  3. }

其他的类似。


Pipelines

golang起的这个名字有点奇怪,当作是 expression (表达式)就好了。有三种使用方法

  • 参数
  • .method [参数 参数 参数 ...]
  • 函数 [参数 参数 参数 ...]

是可以直接调用方法或函数的,不过不需要加小括号。


发表于 2016-04-07 16:24 PM,最后更新于 2018-11-09 11:39:34 AM。

本文使用 署名 - 非商业性使用 - 相同方式共享 4.0 国际 协议


评论加载中...

首页