不思量 發表於 2020-7-8 17:15:00

go template使用

<p>以text/template为例, 而html/template的接口与前者一样,不再缀述。</p>
<p>模板文件一般由<code>.tmpl</code>或<code>.tpl</code>为后缀。<br>
<strong>一些名词</strong></p>
<blockquote>
<p>dot:用表示<code>.</code>,相当于一个变量,保存着传进来的值,可以改变<br>
pipeline:从字面上看,有点像管道<code>|</code>,但从文档上看,实际上指的是一切取值操作,包括<code>{{ . }}</code>、<code>{{ $name }}</code>,而<code>|</code>与unix中的一样:作为函数的最后一个参数<br>
{{}}:相当于占位符,主要的逻辑都写在里面</p>
</blockquote>
<h1 id="1-模板定义">1 模板定义</h1>
<h2 id="11-取值">1.1 取值</h2>
<p>取值的作用主要是在页面中表示出来,或者使用一个变量保存</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>方式</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>当前值</td>
<td><code>{{ . }}</code></td>
<td>传什么值,就取什么值,假如直接在页面上输出的话,类似fmt.Println</td>
</tr>
<tr>
<td>结构体</td>
<td><code>{{ .Field }}</code></td>
<td><code>Field</code>指的是字段名,假如结构体嵌套,还可以再使用<code>.</code>取值,注意:遵循可见性规则</td>
</tr>
<tr>
<td>变量</td>
<td><code>{{ $varName }}</code></td>
<td>以<code>$</code>开头,取出变量的值,如何定义且看<code>1.2</code></td>
</tr>
<tr>
<td>字典</td>
<td><code>{{ .key }}</code></td>
<td>取字典key对应的值,不需要首字母大写,嵌套时,可以再使用<code>.</code>取值</td>
</tr>
<tr>
<td>无参数方法</td>
<td><code>{{ .Method }}</code></td>
<td>执行Method这个方法,第一个返回值作为取出的值,注意:遵循可见性规则,而且返回值有要求,详细见xxx</td>
</tr>
<tr>
<td>无参数函数</td>
<td><code>{{ func}}</code></td>
<td>执行func(),把返回值当做结果,详见xxx</td>
</tr>
</tbody>
</table>
<h2 id="12-变量">1.2 变量</h2>
<p>有些值,我们可能需要重复使用,最好的方法就是使用一个变量来保存值减少重复求值的过程。</p>
<pre><code class="language-golang">// 用到的数据
name := "abcdef"
</code></pre>
<p>假如我们把name传进来,那么假如要求其长度并将其保存起来,可以使用一个内置函数(见1.4):<code>len</code><br>
在go template中,用<code>$</code>表示变量,有点类似shell,使用:</p>
<pre><code class="language-bash">{{ $lenght := len . }}
&lt;h1&gt;长度:{{ $lenght }}&lt;/h1&gt;
</code></pre>
<p>实际上,还可以这样写:</p>
<pre><code class="language-bash">{{ $lenght := . | len }}
&lt;h1&gt;长度:{{ $lenght }}&lt;/h1&gt;
</code></pre>
<p>利用<code>|</code>把<code>abcdef</code>当做最后一个参数传给<code>len</code></p>
<h2 id="13-动作">1.3 动作</h2>
<p>go template的动作(action)有点像,django的模板引擎中的tag,不过两者之间还是有较大的不一样。</p>
<h3 id="131-注释">1.3.1 注释</h3>
<p>注释,执行时会忽略。可以多行。注释不能嵌套,并且必须紧贴分界符始止,就像这里表示的一样。<br>
<code>{{/* 我是注释啊 */}}</code></p>
<h3 id="132-if判断">1.3.2 if判断</h3>
<p>有以下3种</p>
<ol>
<li><code>{{if pipeline}} T1 {{end}}</code><br>
如果pipeline的值为empty,不产生输出,否则输出T1执行结果。不改变dot的值。<br>
Empty值包括false、0、任意nil指针或者nil接口,任意长度为0的数组、切片、字典。<br>
如:</li>
</ol>
<pre><code class="language-jinja">&lt;p&gt;{{ if . }}welcome{{ end }}&lt;/p&gt;
</code></pre>
<p>在这里我传的是一个bool值,为true,因此p便签中的内容为welcome</p>
<ol start="2">
<li><code>{{if pipeline}} T1 {{else}} T0 {{end}}</code><br>
如果pipeline的值为empty,输出T0执行结果,否则输出T1执行结果。不改变dot的值。</li>
</ol>
<pre><code class="language-jinja">&lt;p&gt;{{ if . }}welcome{{ else }}Get out!{{ end }}&lt;/p&gt;

</code></pre>
<ol start="3">
<li><code>{{if pipeline}} T1 {{else if pipeline}} T0 {{end}}</code><br>
用于简化if-else链条,else action可以直接包含另一个if;等价于:</li>
</ol>
<pre><code class="language-jinja">&lt;p&gt;{{ if eq . 1 }}count=1{{ else if eq . 2}}count=2{{ else if eq . 3}}count=3{{ end }}&lt;/p&gt;
</code></pre>
<p>这里的<code>eq</code>是一个内置函数,相当于<code>==</code>。</p>
<h3 id="133-with">1.3.3 with</h3>
<p>这里的<code>with</code>与并不是Python的with。go template的<code>with</code>相当于可以暂时修改dot的if。</p>
<p><strong>形式一</strong>: <code>{{with pipeline}} T1 {{end}}</code><br>
如果pipeline为empty不产生输出,否则将dot设为pipeline的值并执行T1。不修改外面的dot。</p>
<pre><code class="language-jinja">{{ with gt . 18}} result:{{ . }}, 嘿嘿嘿 {{end}}
</code></pre>
<p>这里的gt相当于<code>&gt;</code>, 因此假如执行成功,那么<code>.</code>必然是<code>true</code>.</p>
<p><strong>形式二</strong>:<code>{{with pipeline}} T1 {{else}} T0 {{end}}</code><br>
如果pipeline为empty,不改变dot并执行T0,否则dot设为pipeline的值并执行T1。不修改外面的dot。<br>
实际上这和上面的一样,就是多了个<code>{{ else }}</code></p>
<pre><code class="language-jinja">{{ with gt . 18}} result:{{ . }}, 嘿嘿嘿 {{else}} 才{{ . }}岁,未成年 {{end}}
</code></pre>
<h3 id="134-遍历">1.3.4 遍历</h3>
<p>遍历的值必须是数组、切片、字典或者通道。</p>
<ol>
<li>简单形式: <code>{{range pipeline}} T1 {{end}}</code></li>
</ol>
<p>如果pipeline的值其长度为0,不会有任何输出;<br>
否则dot依次设为数组、切片、字典或者通道的每一个成员元素并执行T1;<br>
如果pipeline的值为字典,且键可排序的基本类型,元素也会按键的顺序排序。<br>
如,要遍历的数据如下:</p>
<pre><code class="language-golang">data := mapstring{
                "张三": "hello",
                "李四": "word",
        }
</code></pre>
<p>在模板文件中定义:</p>
<pre><code class="language-jinja">&lt;div&gt;
    {{ range . }}
    &lt;p&gt;{{ . }}&lt;/p&gt;
    {{ end }}
&lt;/div&gt;
</code></pre>
<p>所得到的结果:</p>
<pre><code class="language-html">&lt;div&gt;
    &lt;p&gt;hello&lt;/p&gt;
    &lt;p&gt;word&lt;/p&gt;
&lt;/div&gt;
</code></pre>
<ol start="2">
<li>加else形式:<code>{{range pipeline}} T1 {{else}} T0 {{end}}</code><br>
如果pipeline的值其长度为0,不改变dot的值并执行T0;否则会修改dot并执行T1。<br>
假如数据是一个空切片<code>[]int{}</code>:</li>
</ol>
<pre><code class="language-jinja">&lt;div&gt;
    {{ range . }}
      &lt;p&gt;{{ . }}&lt;/p&gt;
    {{ else }}
      &lt;span&gt;no data&lt;/span&gt;
    {{ end }}
&lt;/div&gt;
</code></pre>
<p>结果是<code>&lt;span&gt;no data&lt;/span&gt;</code></p>
<h3 id="135-嵌套与继承">1.3.5 嵌套与继承</h3>
<h4 id="define">define</h4>
<p>当解析模板时,可以定义另一个模板,该模板会和当前解析的模板相关联。<strong>模板必须定义在当前模板的最顶层</strong>,就像go程序里的全局变量一样。<br>
这种定义模板的语法是将每一个子模板的声明放在"define"和"end" action内部。<br>
如:</p>
<pre><code class="language-jinja">{{ define "rd"}}
    &lt;div&gt;
    {{ range . }}
      &lt;p&gt;{{ . }}&lt;/p&gt;
    {{ else }}
      &lt;span&gt;v2 no data&lt;/span&gt;
    {{ end }}
&lt;/div&gt;
{{ end }}
</code></pre>
<p>注意:结尾<code>{{ end }}</code>和<code>define</code>后面的是字符串</p>
<h4 id="template">template</h4>
<p><code>template</code>就是对<code>define</code>定义的模板或其他模板文件的引用。<br>
<strong>template的形式</strong></p>
<ul>
<li><code>{{template "name"}}</code><br>
执行名为name的模板,提供给模板的参数为nil,如模板不存在输出为""</li>
<li><code>{{template "name" pipeline}}</code><br>
执行名为name的模板,提供给模板的参数为pipeline的值。</li>
</ul>
<p>如,在当前文件中引用:</p>
<pre><code class="language-jinja">{{ define "rd"}}
    &lt;div&gt;
    {{ range . }}
      &lt;p&gt;{{ . }}&lt;/p&gt;
    {{ else }}
      &lt;span&gt;v2 no data&lt;/span&gt;
    {{ end }}
&lt;/div&gt;
{{ end }}

&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;title&gt;template&lt;/title&gt;
&lt;/head&gt;

&lt;body&gt;
{{/*引用*/}}
{{ template "rd"}}
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><strong>template引用其他文件注意:</strong></p>
<ol>
<li>千万注意,要在代码中把文件读进来。<br>
<code>t, _ := template.ParseFiles("./h1.tpl", "./h2.tpl")</code><br>
也可以使用其他函数</li>
<li>template后面跟的是完整的文件名<br>
在h1.tpl中:<code>{{ template "h2.tpl" }}</code></li>
</ol>
<p><strong><code>{{template "name" pipeline}}</code>形式</strong><br>
就是把define中或文件中的<code>.</code>替换成template传进去的值,假如不指定的话,使用当前文件的<code>.</code></p>
<pre><code class="language-jinja">{{ define "say"}}
    &lt;h1&gt;say {{ . }}&lt;/h1&gt;
{{ end }}
{{ template "say" "hi"}}
</code></pre>
<p>结果:<code>&lt;h1&gt;say hi&lt;/h1&gt;</code></p>
<h4 id="block">block</h4>
<p>block是定义模板<code>{{define "name"}} T1 {{end}}</code>和执行<code>{{template "name" pipeline}}</code>缩写,典型的用法是定义一组根模板,然后通过在其中重新定义块模板进行自定义。<br>
如,在<code>./templates/base.tpl</code>中,定义:</p>
<pre><code class="language-jinja">&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;title&gt;Go Templates&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="container-fluid"&gt;
    {{block "content" . }}{{end}}
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>而在其他的模板文件中:</p>
<pre><code class="language-jinja">{{template "base.tpl"}}

{{/* 使用 */}}
{{define "content"}}
        &lt;!-- 写入自己的代码 --&gt;
    &lt;div&gt;Hello world!&lt;/div&gt;
{{end}}
</code></pre>
<p><strong>同样要注意,在解析文件时把多个模板文件传进来</strong></p>
<h3 id="136-去空">1.3.6 去空</h3>
<p><code>{{- . -}}</code><br>
使用{{-语法去除模板内容左侧的所有空白符号, 使用-}}去除模板内容右侧的所有空白符号。</p>
<h2 id="14-函数">1.4 函数</h2>
<p>执行模板时,函数从两个函数字典中查找:首先是模板函数字典,然后是全局函数字典。一般不在模板内定义函数,而是使用Funcs方法添加函数到模板里。</p>
<h3 id="141-一般函数">1.4.1 一般函数</h3>
<ul>
<li>and<br>
函数返回它的第一个empty参数或者最后一个参数;<br>
就是说"and x y"等价于"if x then y else x";所有参数都会执行;<br>
<em>和上面一样:Empty值包括false、0、任意nil指针或者nil接口,任意长度为0的数组、切片、字典。下面再重复</em></li>
</ul>
<p>如:</p>
<pre><code class="language-jinja">{{ and 1 0 }}
{{/* 返回0 */}}

{{ and 1 1 1}}
{{/* 返回1 */}}
</code></pre>
<ul>
<li>or<br>
返回第一个非empty参数或者最后一个参数;<br>
亦即"or x y"等价于"if x then x else y";所有参数都会执行;</li>
</ul>
<p>如:</p>
<pre><code class="language-jinja">{{ or 1 0 }}
{{/* 返回1 */}}

{{ or 0 2 1}}
{{/* 返回2 */}}
</code></pre>
<ul>
<li>not<br>
返回它的单个参数的布尔值的否定</li>
</ul>
<p>如:</p>
<pre><code class="language-jinja">{{ not 1 }}
{{/* 返回false */}}

{{ not 0 }}
{{/* 返回true */}}
</code></pre>
<ul>
<li>len<br>
返回它的参数的整数类型长度</li>
</ul>
<p>如:</p>
<pre><code class="language-jinja">{{/*. 为"abcdef"*/}}
{{ len . }}

{{/*返回6*/}}
</code></pre>
<ul>
<li>index<br>
执行结果为第一个参数以剩下的参数为索引/键指向的值;<br>
如"index x 1 2 3"返回x的值;每个被索引的主体必须是数组、切片或者字典。</li>
</ul>
<p>假如数据为:</p>
<pre><code class="language-golang">        data := [][]int{
                {1, 2, 3, 4, 5,},
                {6, 7, 8, 9, 10,},
        }
</code></pre>
<pre><code class="language-jinja">{{ index . 0 1}}

{{/* 结果为2 */
</code></pre>
<ul>
<li>
<p>print<br>
即fmt.Sprint<br>
S系列函数会把传入的数据生成并返回一个字符串。以下两个相同。</p>
</li>
<li>
<p>printf<br>
即fmt.Sprintf</p>
</li>
<li>
<p>println<br>
即fmt.Sprintln</p>
</li>
<li>
<p>html<br>
返回与其参数的文本表示形式等效的转义HTML。<br>
这个函数在<code>html/template</code>中<strong>不可用</strong>。</p>
</li>
<li>
<p>urlquery<br>
以适合嵌入到网址查询中的形式返回其参数的文本表示的转义值。<br>
这个函数在<code>html/template</code>中<strong>不可用</strong>。</p>
</li>
<li>
<p>js<br>
返回与其参数的文本表示形式等效的转义JavaScript。</p>
</li>
<li>
<p>call<br>
执行结果是调用第一个参数的返回值,该参数必须是函数类型,其余参数作为调用该函数的参数;<br>
如<code>{{ call .X.Y 1 2 }}</code>等价于go语言里的<code>dot.X.Y(1, 2)</code>;<br>
其中Y是函数类型的字段或者字典的值,或者其他类似情况;<br>
call的第一个参数的执行结果必须是函数类型的值(和预定义函数如print明显不同);<br>
该函数类型值必须有1到2个返回值,如果有2个则后一个必须是error接口类型;<br>
如果有2个返回值的方法返回的error非nil,模板执行会中断并返回给调用模板执行者该错误;</p>
</li>
</ul>
<h3 id="142-布尔函数">1.4.2 布尔函数</h3>
<p>布尔函数会将任何类型的零值视为假,其余视为真。</p>
<table>
<thead>
<tr>
<th>函数</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>eq</td>
<td>如果arg1 == arg2则返回真</td>
</tr>
<tr>
<td>ne</td>
<td>如果arg1 != arg2则返回真</td>
</tr>
<tr>
<td>lt</td>
<td>如果arg1 &lt; arg2则返回真</td>
</tr>
<tr>
<td>le</td>
<td>如果arg1 &lt;= arg2则返回真</td>
</tr>
<tr>
<td>gt</td>
<td>如果arg1 &gt; arg2则返回真</td>
</tr>
<tr>
<td>ge</td>
<td>如果arg1 &gt;= arg2则返回真</td>
</tr>
</tbody>
</table>
<p><strong>注意:</strong><br>
为了简化多参数相等检测,eq(只有eq)可以接受2个或更多个参数,它会将第一个参数和其余参数依次比较,返回下式的结果:</p>
<p><code>arg1 == arg2 || arg1 ==arg3 || arg1==arg4 ... </code><br>
(和go的||不一样,不做惰性运算,所有参数都会执行)</p>
<p>比较函数只适用于基本类型(或重定义的基本类型,如"type Celsius float32")。它们实现了go语言规则的值的比较,但具体的类型和大小会忽略掉,因此<em>任意类型有符号整数值都可以互相比较;任意类型无符号整数值都可以互相比较;等等。但是,整数和浮点数不能互相比较。</em></p>
<h3 id="143-自定义函数">1.4.3 自定义函数</h3>
<p>使用<code>Funcs</code>方法,可以将自定义好的函数放入到模板中。<br>
<code>Funcs</code>的签名:<br>
<code>func (t *Template) Funcs(funcMap FuncMap) *Template</code><br>
Funcs方法向模板t的函数字典里加入参数funcMap内的键值对。<br>
如果funcMap某个键值对的值不是函数类型或者返回值不符合要求会panic。<br>
但是,可以对t函数列表的成员进行重写。方法返回t以便进行链式调用。</p>
<p>例子:<br>
<em><strong>例子中的使用的一些方法,见第2部分</strong></em></p>
<pre><code class="language-golang">// 1. 定义函数,首字母可以小写,注意返回值
func SayHi(char string) (string, error) {
        return "Hi" + char, nil

}

func indexFunc(w http.ResponseWriter, r *http.Request) {
        // 2. new
        t := template.New("hello.tpl")
        // 3. 加入t的函数列表,需要替换掉t
        t = t.Funcs(template.FuncMap{"sayHi": SayHi})
        // 4. Parse 可以是文件也可以是字符串
        t, _ = t.ParseFiles("./hello.tpl")

        userName := "xxx"
        // 5. 渲染
        _ = t.Execute(w, userName)
}
</code></pre>
<p>上面代码的2-4步,可以使用一段链式调用完成:</p>
<pre><code class="language-golang">        t, _ := template.New("hello.tpl").Funcs(template.FuncMap{"sayHi": SayHi}).ParseFiles("./hello.tpl")
</code></pre>
<p><strong>注意事项</strong><br>
<code>template.New</code>的文件名应该和要渲染的文件名一样<br>
自定义函数有1-2个返回值,第一个值当做正式返回值。假如有第二个返回值:用来<code>panic</code>,其类型必须是<code>error</code>,当对应的值非<code>nil</code>时,<code>panic</code></p>
<h1 id="2-一些常用的方法">2 一些常用的方法</h1>
<p>模板引擎的使用,一般有如下三步:</p>
<ol>
<li>定义模板文件</li>
<li>解析模板文件</li>
<li>模板渲染</li>
</ol>
<p>其中,第2、3步都要用到一些template的方法(这里用的是text/template)</p>
<h2 id="21-解析模板文件的方法">2.1 解析模板文件的方法</h2>
<pre><code class="language-golang">// 解析字符串
func (t *Template) Parse(src string) (*Template, error)

// 解析1个或多个文件
func ParseFiles(filenames ...string) (*Template, error)

// 解析用正则匹配到的文件
func ParseGlob(pattern string) (*Template, error)
</code></pre>
<h3 id="使用">使用</h3>
<h4 id="1-parse">1. Parse</h4>
<p>这里使用<code>New</code>函数:<br>
<code>func New(name string) *Template</code><br>
其作用是创建一个名为name的模板。</p>
<pre><code class="language-golang">t, _ := template.New("test.tpl").Parse("&lt;h1&gt;{{ . }}&lt;/h1&gt;")
</code></pre>
<p>Parse可以多次调用,但只有第一次调用可以包含空格、注释和模板定义之外的文本。<br>
如果后面的调用在解析后仍剩余文本会引发错误、返回nil且丢弃剩余文本;<br>
如果解析得到的模板已有相关联的同名模板,会覆盖掉原模板。</p>
<h4 id="2-parsefiles">2. ParseFiles</h4>
<pre><code class="language-golang">t, _ := template.ParseFiles("./h1.tpl", "./h2.tpl", "./h3.tpl")
</code></pre>
<p>解析匹配参数中的文件里的模板定义并将解析结果与t关联。<br>
如果发生错误,会停止解析并返回nil,否则返回(t, nil)。至少要存在一个匹配的文件。</p>
<h4 id="3-parseglob">3. ParseGlob</h4>
<pre><code class="language-golang">t, _ := template.ParseGlob("./*.tpl")
</code></pre>
<p>解析当前目录下,所有以<code>.tpl</code>结尾的文件,假如有专门的文件夹存放模板文件,可以使用<code>templates/*.tmpl</code>(1层目录时)和<code>templates/**/*.tmpl</code>(2层目录时)</p>
<p>匹配时,和<code>ParseFiles</code>一样。</p>
<h2 id="22-模板渲染的方法">2.2 模板渲染的方法</h2>
<pre><code class="language-golang">func (t *Template) Execute(wr io.Writer, data interface{}) error
// Execute方法将解析好的模板应用到data上,并将输出写入wr。
// 如果执行时出现错误,会停止执行,但有可能已经写入wr部分数据。
// 模板可以安全的并发执行。

func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error
// 类似Execute,但是使用名为name的t关联的模板产生输出。
</code></pre>
<p><code>Execute</code>渲染的是<code>ParseFiles</code>或<code>ParseGlob</code>得到的第一个文件,假如要读取多个文件时,就有可能渲染的不是想要的文件,所以需要使用<code>ExecuteTemplate</code>指定一个已经解析的文件。</p>
<p>如:</p>
<pre><code class="language-golang">t, _ := template.ParseFiles("./h1.tpl", "./h2.tpl")
userName := "xxx"
_ = t.Execute(w, userName)
</code></pre>
<p>怎么样都是渲染<code>h1.tpl</code>,假如要渲染<code>h2.tpl</code>:</p>
<pre><code class="language-golang">t, _ := template.ParseFiles("./h1.tpl", "./h2.tpl")
userName := "xxx"
_ = t.ExecuteTemplate(w, "h2.tpl", userName)
</code></pre>
<p><strong>注意</strong><br>
<code>ExecuteTemplate</code>的name可以是<code>define</code>的模块名<br>
如:</p>
<pre><code class="language-golang">t, _ := template.New("test").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
_ = t.ExecuteTemplate(out, "T", "word")
</code></pre>
<p>当然,用其他解析方法也可以。</p>
<h1 id="3-htmltemplate的不同之处">3 html/template的不同之处</h1>
<p>由于<code>html/template</code>的API和<code>text/template</code>的API是一样的,解析和渲染没有什么不一样,但是在定义模板时,考虑到网站的安全性,会对一些风险内容进行转义,因此会有<code>text/template</code>有点差别。</p>
<p>如:</p>
<pre><code class="language-golang">t, _ := template.New("test").Parse("{{ . }}")
char := "&lt;script&gt;alert('you have been pwned')&lt;/script&gt;!"
_ = t.Execute(w, char)
</code></pre>
<p>得到的结果是:<code>&amp;lt;script&amp;gt;alert(&amp;#39;you have been pwned&amp;#39;)&amp;lt;/script&amp;gt;!</code><br>
与预期不符,为此,<code>html/template</code>有一个函数可以专门处理这些我们认为安全的字符串:<code>template.HTML</code>。<br>
再使用时,我们可以自定义一个<code>safe</code>函数,和其他模板引擎一样,不对一些字符串转义。</p>
<pre><code class="language-golang">func safe(s string) template.HTML {
        return template.HTML(s)
}
</code></pre>
<p>然后使用:</p>
<pre><code class="language-golang">t, _ := template.New("test").Funcs(template.FuncMap{"safe": safe}).Parse("{{ . | safe }}")

_ = t.Execute(w, char)
</code></pre>
<h2 id="补充">补充</h2>
<p>如果<code>{{.}</code>}是非字符串类型的值,可以用于JavaScript上下文环境里:<br>
<code>struct{A,B string}{ "foo", "bar" }</code><br>
将该值应用在在转义后的模板里:</p>
<p><code>&lt;script&gt;var pair = {{.}};&lt;/script&gt;</code><br>
模板输出为:<br>
<code>&lt;script&gt;var pair = {"A": "foo", "B": "bar"};&lt;/script&gt;</code></p>
<p>参考:</p>
<ol>
<li>https://studygolang.com/static/pkgdoc/pkg/text_template.htm</li>
<li>https://www.liwenzhou.com/posts/Go/go_template/</li>
</ol>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:403·Forbidden,转载请注明原文链接:https://www.cnblogs.com/lczmx/p/13268081.html</p><br><br>
来源:https://www.cnblogs.com/lczmx/p/13268081.html
頁: [1]
查看完整版本: go template使用