содержание
Официальное определение:
Package template implements data-driven templates for generating textual output.
Пакет шаблонов — это управляемый данными шаблон вывода текста, который фактически заполняет данные в письменном шаблоне.
шаблон
Что такое шаблон?
Вот простой пример шаблона:
// 模板定义
tepl := "My name is {{ . }}"
// 解析模板
tmpl, err := template.New("test").Parse(tepl)
// 数据驱动模板
data := "jack"
err = tmpl.Execute(os.Stdout, data)
период между {{ и }}.
Представляет данные, переданные в шаблон, и отображает различное содержимое в соответствии с входящими данными.
.
Может представлять любой тип на языке go, например, структуры, хэши и т. д.
Что касается содержимого пакетов {{ и }}, то они в совокупности называются действиями, которые делятся на два типа:
- оценки данных
- структуры управления
Результат оценки действия будет скопирован непосредственно в шаблон.Структура управления аналогична структуре программы Go, которую мы написали, включая условные операторы, операторы цикла, переменные, вызовы функций и т. д.
После того, как шаблон успешно проанализирован (Parse), его можно безопасно использовать в параллельной среде, если вывод осуществляется в тот жеio.Writer
Данные могут перекрываться (поскольку порядок параллельного выполнения не гарантируется).
Actions
В шаблоне не так много действий, рассмотрим их по порядку.
Примечания
{{/* comment */}}
Обрезать пробелы
// 裁剪 content 前后的空格
{{- content -}}
// 裁剪 content 前面的空格
{{- content }}
// 裁剪 content 后面的空格
{{ content -}}
вывод текста
{{ pipeline }}
Данные, представленные конвейером, будут сгенерированы и вызваныfmt.Print
Функционально-подобный вывод, такой как целочисленный тип 3, будет преобразован в вывод строки "3".
Условные операторы
{{ if pipeline }} T1 {{ end }}
{{ if pipeline }} T1 {{ else }} T0 {{ end }}
{{ if pipeline }} T1 {{ else if pipeline }} T0 {{ end }}
// 上面的语法其实是下面的简写
{{ if pipeline }} T1 {{ else }}{{ if pipeline }} T0 { {end }}{{ end }}
{{ if pipeline }} T1 {{ else if pipeline }} T2 {{ else }} T0 {{ end }}
Если значение конвейера пусто, T1 не будет выведено, иначе будет выведено T1.
Нулевые значения — это false, 0, произвольные нулевые указатели, значения интерфейса, массивы, срезы, словари и пустые строки.""
(строка длины 0).
оператор цикла
{{ range pipeline }} T1 {{ end }}
// 这个 else 比较有意思,如果 pipeline 的长度为 0 则输出 else 中的内容
{{ range pipeline }} T1 {{ else }} T0 {{ end }}
// 获取容器的下标
{{ range $index, $value := pipeline }} T1 {{ end }}
Значение конвейера должно быть одним из массива, среза, словаря и канала, то есть значением итерируемого типа, и выводить несколько T1 в соответствии с длиной значения.
define
{{ define "name" }} T {{ end }}
Определите шаблон с именем name.
template
{{ template "name" }}
{{ template "name" pipeline }}
Относится к шаблону с именем name.
block
{{ block "name" pipeline }} T1 {{ end }}
Семантика блока заключается в том, что если есть шаблон с именем name, на него будут ссылаться и он будет выполнен, а если нет шаблона с именем name, он выполнит содержимое, определенное им самим.
То есть делается еще один шаг, чтобы определить, существует ли шаблон, и в соответствии с этим результатом отрисовывается другой контент.
with
{{ with pipeline }} T1 {{ end }}
// 如果 pipeline 是空值则输出 T0
{{ with pipeline }} T1 {{ else }} T0 {{ end }}
{{ with arg }}
. // 此时 . 就是 arg
{{ end }}
with создает новый контекст, в котором.
снаружи.
Это не имеет значения.
параметр
Значение параметра имеет несколько представлений и может оценивать любой тип, включая функции, указатели (указатели автоматически косвенно оцениваются до исходного значения):
- Логические значения, строки, символы, числа с плавающей запятой, комплексные числа ведут себя как Go
- ключевые слова
nil
Представляет на языке gonil
- Период символа представляет собой результат значения
- Переменные, начинающиеся с символа $, являются соответствующими значениями переменной
- Поля структуры представлены в виде
.Field
, результатом является значение Field, которое поддерживает связанные вызовы.Field1.Field2
- Ключ словаря представлен как
.Key
Результатом является значение, соответствующее ключу - Если это метод в наборе методов структуры .Method Результатом является значение вызова метода с точкой в качестве получателя **
- Метод либо имеет только одно возвращаемое значение любого типа, либо второе возвращаемое значение является ошибкой, не более того, если ошибка не равна нулю, он напрямую сообщит об ошибке и остановит рендеринг шаблона.
- Результат вызова метода может продолжаться в цепочке
.Field1.Key1.Method1.Field2.Key2.Method2
- Объявление набора переменных методов также может быть вызвано
$x.Method1.Field
- Групповые вызовы в скобках
print (.Func1 arg1) (.Func2 arg2)
или(.StructValuedMethod "arg").Field
Пожалуй, самое сложное для понимания здесь — это способ вызова функции.Если вы обращаетесь к функции в наборе методов структуры и к функции в поле, какая разница в поведении на данный момент?
Напишите демо для проверки:
type T struct {
Add func(int) int
}
func (t *T) Sub(i int) int {
log.Println("get argument i:", i)
return i - 1
}
func arguments() {
ts := &T{
Add: func(i int) int {
return i + 1
},
}
tpl := `
// 只能使用 call 调用
call field func Add: {{ call .ts.Add .y }}
// 直接传入 .y 调用
call method func Sub: {{ .ts.Sub .y }}
`
t, _ := template.New("test").Parse(tpl)
t.Execute(os.Stdout, map[string]interface{}{
"y": 3,
"ts": ts,
})
}
output:
call field func Add: 4
call method func Sub: 2
Можно сделать вывод, что если функция является функциональным полем в структуре, функция не будет вызываться автоматически, можно использовать только встроенные функцииcall
перечислить.
Если функция является методом в наборе методов структуры, метод будет вызываться автоматически, а возвращаемое значение будет присвоено.
, если функция возвращает новую структуру, карту, вы можете продолжить цепочку вызовов.
Переменная
Конвейер в экшене может инициализировать переменную для сохранения результата, и синтаксис тоже очень простой:
$variable = pipeline
В этот момент действие объявляет переменную, не производя никакого вывода.
Цикл диапазона может объявить две переменные:
range $index, $element := pipeline
В if, with и range область действия переменной распространяется на то место, где находится {{ end }}.
Если это не управляющая структура, область объявленной переменной распространяется на весь шаблон.
Например, чтобы объявить переменную в начале шаблона:
{{ $pages := .pagination.Pages }}
{{ $current := .pagination.Current }}
Когда начинается рендеринг,$
переменная будет заменена на.
начальное значение, например$pages
будет заменен на.pagenation.Pages
. Поэтому взаимные ссылки между шаблонами не передают переменные, а переменные работают только в определенной области.
функция
Рендеринг шаблона ищет функции в двух местах:
- пользовательская карта функций
- глобальная карта функций, эти функции являются встроенными шаблонами
Использование пользовательских функцийfunc (t *Template) Funcs(funcMap FuncMap) *Template
регистр.
Список глобальных функций:
and
Возвращает результат булевой операции между параметрами, которая на самом деле является логическим оператором в JavaScript.&&
, возвращает первое значение, которое можно преобразовать в false , которое в Go равно нулю, и возвращает последнее значение, если оба значения верны.
tpl := "{{ and .x .y .z }}"
t, _ := template.New("test").Parse(tpl)
t.Execute(os.Stdout, map[string]interface{}{
"x": 1,
"y": 0,
"z": 3,
})
output:
0
or
логический оператор||
, возвращает первое значение, которое можно преобразовать в значение true, которое в Go является ненулевым значением, и возвращает последнее значение, если оба значения ложны.
tpl := "{{ or .x .y .z }}"
t, _ := template.New("test").Parse(tpl)
t.Execute(os.Stdout, map[string]interface{}{
"x": 1,
"y": 0,
"z": 3,
})
output:
1
call
Возвращает результат вызова первого параметра функции, функция должна иметь одно или два возвращаемых значения (второе возвращаемое значение должно быть ошибкой, если значение не равно нулю, рендеринг шаблона остановится)
tpl := "call: {{ call .x .y .z }} \n"
t, _ := template.New("test").Parse(tpl)
t.Execute(os.Stdout, map[string]interface{}{
"x": func(x, y int) int { return x+y},
"y": 2,
"z": 3,
})
output:
5
html
Возвращает экранированную строку HTML, эту функцию нельзя использовать вhtml/template
используется в.
js
Возвращает экранированную строку JavaScript.
index
Используется, когда первым параметром является массив, срез или карта, и возвращает значение соответствующего нижнего индекса.
index x 1 2 3
равныйx[1][2][3]
.
len
Возвращает длину составного типа.
not
Возвращает инверсию параметра логического типа.
равныйfmt.Sprint
.
printf
равныйfmt.Sprintf
.
println
равныйfmt.Sprintln
.
urlquery
URL-запрос экранирует строку, а не вhtml/template
используется в упаковке.
// URLQueryEscaper returns the escaped value of the textual representation of
// its arguments in a form suitable for embedding in a URL query.
func URLQueryEscaper(args ...interface{}) string {
return url.QueryEscape(evalArgs(args))
}
Вы можете видеть из исходного кода, что эта функция вызывается напрямуюurl.QueryEscape
В экранировании строк нет ничего загадочного.
функция сравнения
-
eq
: == -
ge
: >= -
gt
: > -
le
: <= -
lt
: < -
ne
: !=
Проанализируйте два исходных кода:
// eq evaluates the comparison a == b || a == c || ...
func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) {
v1 := indirectInterface(arg1)
k1, err := basicKind(v1)
if err != nil {
return false, err
}
if len(arg2) == 0 {
return false, errNoComparison
}
for _, arg := range arg2 {
v2 := indirectInterface(arg)
k2, err := basicKind(v2)
if err != nil {
return false, err
}
truth := false
if k1 != k2 {
// Special case: Can compare integer values regardless of type's sign.
switch {
case k1 == intKind && k2 == uintKind:
truth = v1.Int() >= 0 && uint64(v1.Int()) == v2.Uint()
case k1 == uintKind && k2 == intKind:
truth = v2.Int() >= 0 && v1.Uint() == uint64(v2.Int())
default:
return false, errBadComparison
}
} else {
switch k1 {
case boolKind:
truth = v1.Bool() == v2.Bool()
case complexKind:
truth = v1.Complex() == v2.Complex()
case floatKind:
truth = v1.Float() == v2.Float()
case intKind:
truth = v1.Int() == v2.Int()
case stringKind:
truth = v1.String() == v2.String()
case uintKind:
truth = v1.Uint() == v2.Uint()
default:
panic("invalid kind")
}
}
if truth {
return true, nil
}
}
return false, nil
}
// ne evaluates the comparison a != b.
func ne(arg1, arg2 reflect.Value) (bool, error) {
// != is the inverse of ==.
equal, err := eq(arg1, arg2)
return !equal, err
}
eq сначала определяет, равны ли типы интерфейсов, а потом определяет, равны ли значения, в этом нет ничего особенного.
ne — это простой вызов eq, затем инвертируется.
ge, gt, le и lt аналогичны экв. Сначала оценивается тип, а затем оценивается размер.
Вложенные шаблоны
Вот более сложный пример:
// 加载模板
template.ParseFiles("templates/")
// 加载多个模板到一个命名空间(同一个命名空间的模块可以互相引用)
template.ParseFiles("header.tmpl", "content.tmpl", "footer.tmpl")
// must 加载失败时 panic
tmpl := template.Must(template.ParseFiles("layout.html"))
// 执行加载后的模板文件,默认执行第一个
tmpl.Execute(w, "test")
// 如果 tmpl 中有很多个模板,可以指定要执行的模板名
tmpl.ExecuteTemplate(w, "layout", "Hello world")
ExecuteTemplate
Указанное имя находится в файле шаблонаdefine "name"
имя.
Суммировать
Parse
серийная функция инициализированаTemplate
Тип экземпляр.
Execute
Ряд функций передает данные в шаблон для отображения окончательной строки.
Шаблоны по сутиParse
Функция для загрузки нескольких файлов в одинTempalte
В экземпляре типа ключевое слово define в файле синтаксического анализа регистрирует именованный шаблон, который можно использовать между именованными шаблонами.template
ссылаться друг на друга,Execute
Передайте соответствующий рендеринг данных.