工具介绍
适用于 Go 的快速、强大但易于使用的模板引擎。针对速度进行了优化,热点路径中不需要分配内存。比 html/template 快 20 倍
# quicktemplate
A fast, powerful, yet easy to use template engine for Go.
Inspired by the [Mako templates](http://www.makotemplates.org/) philosophy.
# Features
* [Extremely fast](#performance-comparison-with-htmltemplate).
Templates are converted into Go code and then compiled.
* Quicktemplate syntax is very close to Go - there is no need to learn
yet another template language before starting to use quicktemplate.
* Almost all bugs are caught during template compilation, so production
suffers less from template-related bugs.
* Easy to use. See [quickstart](#quick-start) and [examples](https://github.com/valyala/quicktemplate/tree/master/examples)
for details.
* Powerful. Arbitrary Go code may be embedded into and mixed with templates.
Be careful with this power - do not query the database and/or external resources from
templates unless you miss the PHP way in Go :) This power is mostly for
arbitrary data transformations.
* Easy to use template inheritance powered by [Go interfaces](https://golang.org/doc/effective_go.html#interfaces).
See [this example](https://github.com/valyala/quicktemplate/tree/master/examples/basicserver) for details.
* Templates are compiled into a single binary, so there is no need to copy
template files to the server.
# Drawbacks
* Templates cannot be updated on the fly on the server, since they
are compiled into a single binary.
Take a look at [fasttemplate](https://github.com/valyala/fasttemplate)
if you need a fast template engine for simple dynamically updated templates.
[There are ways](https://www.reddit.com/r/golang/comments/f290ja/hot_reloading_with_quicktemplates_sqlc_and/) to dynamically update the templates during development.
# Performance comparison with html/template
Quicktemplate is more than 20x faster than [html/template](https://golang.org/pkg/html/template/).
The following simple template is used in the benchmark:
* [html/template version](https://github.com/valyala/quicktemplate/blob/master/testdata/templates/bench.tpl)
* [quicktemplate version](https://github.com/valyala/quicktemplate/blob/master/testdata/templates/bench.qtpl)
Benchmark results:
```
…
```
[goTemplateBenchmark](https://github.com/SlinSo/goTemplateBenchmark) compares QuickTemplate with numerous Go templating packages. QuickTemplate performs favorably.
# Security
* All template placeholders are HTML-escaped by default.
* Template placeholders for JSON strings prevent from ``-based
XSS attacks:
```qtpl
{% func FailedXSS() %}
{% endfunc %}
```
# Examples
See [examples](https://github.com/valyala/quicktemplate/tree/master/examples).
# Quick start
First of all, install the `quicktemplate` package
and [quicktemplate compiler](https://github.com/valyala/quicktemplate/tree/master/qtc) (`qtc`):
```
go get -u github.com/valyala/quicktemplate
go get -u github.com/valyala/quicktemplate/qtc
```
If you using `go generate`, you just need put following into your `main.go`
Important: please specify your own folder (-dir) to generate template file
```
//go:generate go get -u github.com/valyala/quicktemplate/qtc
//go:generate qtc -dir=app/views
```
Let's start with a minimal template example:
```qtpl
All text outside function templates is treated as comments,
i.e. it is just ignored by quicktemplate compiler (`qtc`). It is for humans.
Hello is a simple template function.
{% func Hello(name string) %}
Hello, {%s name %}!
{% endfunc %}
```
Save this file into a `templates` folder under the name `hello.qtpl`
and run `qtc` inside this folder.
If everything went OK, `hello.qtpl.go` file should appear in the `templates` folder.
This file contains Go code for `hello.qtpl`. Let's use it!
Create a file main.go outside `templates` folder and put the following
code there:
```go
package main
import (
"fmt"
"./templates"
)
func main() {
fmt.Printf("%s\n", templates.Hello("Foo"))
fmt.Printf("%s\n", templates.Hello("Bar"))
}
```
Then issue `go run`. If everything went OK, you'll see something like this:
```
Hello, Foo!
Hello, Bar!
```
Let's create more a complex template which calls other template functions,
contains loops, conditions, breaks, continues and returns.
Put the following template into `templates/greetings.qtpl`:
```
…
```
Run `qtc` inside `templates` folder. Now the folder should contain
two files with Go code: `hello.qtpl.go` and `greetings.qtpl.go`. These files
form a single `templates` Go package. Template functions and other template
stuff is shared between template files located in the same folder.
So `Hello` template function may be used inside `greetings.qtpl` while
it is defined in `hello.qtpl`.
Moreover, the folder may contain ordinary Go files, so its contents may
be used inside templates and vice versa.
The package name inside template files may be overriden
with `{% package packageName %}`.
Now put the following code into `main.go`:
```go
package main
import (
"bytes"
"fmt"
"./templates"
)
func main() {
names := []string{"Kate", "Go", "John", "Brad"}
// qtc creates Write* function for each template function.
// Such functions accept io.Writer as first parameter:
var buf bytes.Buffer
templates.WriteGreetings(&buf, names)
fmt.Printf("buf=\n%s", buf.Bytes())
}
```
Careful readers may notice different output tags were used in these
templates: `{%s name %}` and `{%= Hello(name) %}`. What's the difference?
The `{%s x %}` is used for printing HTML-safe strings, while `{%= F() %}`
is used for embedding template function calls. Quicktemplate supports also
other output tags:
* `{%d int %}` and `{%dl int64 %}` `{%dul uint64 %}` for integers.
* `{%f float %}` for float64.
Floating point precision may be set via `{%f.precision float %}`.
For example, `{%f.2 1.2345 %}` outputs `1.23`.
* `{%z bytes %}` for byte slices.
* `{%q str %}` and `{%qz bytes %}` for JSON-compatible quoted strings.
* `{%j str %}` and `{%jz bytes %}` for embedding str into a JSON string. Unlike `{%q str %}`,
it doesn't quote the string.
* `{%u str %}` and `{%uz bytes %}` for [URL encoding](https://en.wikipedia.org/wiki/Percent-encoding)
the given str.
* `{%v anything %}` is equivalent to `%v` in [printf-like functions](https://golang.org/pkg/fmt/).
All the output tags except `{%= F() %}` produce HTML-safe output, i.e. they
escape `<` to `<`, `>` to `>`, etc. If you don't want HTML-safe output,
then just put `=` after the tag. For example: `{%s= "
This h1 won't be escaped
" %}`.
As you may notice `{%= F() %}` and `{%s= F() %}` produce the same output for `{% func F() %}`.
But the first one is optimized for speed - it avoids memory allocations and copies.
It is therefore recommended to stick to it when embedding template function calls.
Additionally, the following extensions are supported for `{%= F() %}`:
* `{%=h F() %}` produces html-escaped output.
* `{%=u F() %}` produces [URL-encoded](https://en.wikipedia.org/wiki/Percent-encoding) output.
* `{%=q F() %}` produces quoted json string.
* `{%=j F() %}` produces json string without quotes.
* `{%=uh F() %}` produces html-safe URL-encoded output.
* `{%=qh F() %}` produces html-safe quoted json string.
* `{%=jh F() %}` produces html-safe json string without quotes.
All output tags except `{%= F() %}` family may contain arbitrary valid
Go expressions instead of just an identifier. For example:
```qtpl
Import fmt for fmt.Sprintf()
{% import "fmt" %}
FmtFunc uses fmt.Sprintf() inside output tag
{% func FmtFunc(s string) %}
{%s fmt.Sprintf("FmtFunc accepted %q string", s) %}
{% endfunc %}
```
There are other useful tags supported by quicktemplate:
* `{% comment %}`
```qtpl
{% comment %}
This is a comment. It won't trap into the output.
It may contain {% arbitrary tags %}. They are just ignored.
{% endcomment %}
```
* `{% plain %}`
```qtpl
{% plain %}
Tags will {% trap into %} the output {% unmodified %}.
Plain block may contain invalid and {% incomplete tags.
{% endplain %}
```
* `{% collapsespace %}`
```qtpl
{% collapsespace %}
and {%s "tags" %}
{% endcollapsespace %}
```
Is converted into:
```
and tags
```
* `{% stripspace %}`
```qtpl
{% stripspace %}
and {%s " tags" %}
{% endstripspace %}
```
Is converted into:
```
and tags
```
* It is possible removing whitespace before and after the tag by adding `-` after `{%` or prepending `%}` with `-`. For example:
```qtpl
var sum int
{%- for i := 1; i <= 3; i++ -%}
sum += {%d i %}
{%- endfor -%}
return sum
```
Is converted into:
```
var sum int
sum += 1
sum += 2
sum += 3
return sum
```
* `{% switch %}`, `{% case %}` and `{% default %}`:
```qtpl
1 + 1 =
{% switch 1+1 %}
{% case 2 %}
2?
{% case 42 %}
42!
{% default %}
I don't know :(
{% endswitch %}
```
* `{% code %}`:
```qtpl
{% code
// arbitrary Go code may be embedded here!
type FooArg struct {
Name string
Age int
}
%}
```
* `{% package %}`:
```qtpl
Override default package name with the custom name
{% package customPackageName %}
```
* `{% import %}`:
```qtpl
Import external packages.
{% import "foo/bar" %}
{% import (
"foo"
bar "baz/baa"
) %}
```
* `{% cat "/path/to/file" %}`:
```qtpl
Cat emits the given file contents as a plaintext:
{% func passwords() %}
/etc/passwd contents:
{% cat "/etc/passwd" %}
{% endfunc %}
```
* `{% interface %}`:
```
…
```
See [basicserver example](https://github.com/valyala/quicktemplate/tree/master/examples/basicserver)
for more details.
# Performance optimization tips
* Prefer calling `WriteFoo` instead of `Foo` when generating template output
for `{% func Foo() %}`. This avoids unnesessary memory allocation and a copy
for a `string` returned from `Foo()`.
* Prefer `{%= Foo() %}` instead of `{%s= Foo() %}` when embedding
a function template `{% func Foo() %}`. Though both approaches generate
identical output, the first approach is optimized for speed.
* Prefer using existing output tags instead of passing `fmt.Sprintf`
to `{%s %}`. For instance, use `{%d num %}` instead
of `{%s fmt.Sprintf("%d", num) %}`, because the first approach is optimized
for speed.
* Prefer using specific output tags instead of generic output tag
`{%v %}`. For example, use `{%s str %}` instead of `{%v str %}`, since
specific output tags are optimized for speed.
* Prefer creating custom function templates instead of composing complex
strings by hands before passing them to `{%s %}`.
For instance, the first approach is slower than the second one:
```qtpl
{% func Foo(n int) %}
{% code
// construct complex string
complexStr := ""
for i := 0; i < n; i++ {
complexStr += fmt.Sprintf("num %d,", i)
}
%}
complex string = {%s= complexStr %}
{% endfunc %}
```
```qtpl
{% func Foo(n int) %}
complex string = {%= complexStr(n) %}
{% endfunc %}
// Wrap complexStr func into stripspace for stripping unnesessary space
// between tags and lines.
{% stripspace %}
{% func complexStr(n int) %}
{% for i := 0; i < n; i++ %}
num{% space %}{%d i %}{% newline %}
{% endfor %}
{% endfunc %}
{% endstripspace %}
```
* Make sure that the `io.Writer` passed to `Write*` functions
is [buffered](https://golang.org/pkg/buf