--- title: "Mustache 模板引擎中文文档" date: 2021-06-19T11:11:55+07:00 draft: false --- ### 前言 Mustache 是一个有趣的引擎,总会让人想起语言界的函数式编程。官网上有关于这个引擎每种语言的实现。实现这词很合适,因为 Mustache 是一个规范,一个标准定义,详细参看 [specs](https://github.com/mustache/spec)。 Mustache 引擎实际应用据我观察是不多的,但这并不能掩盖它的美感。 [Mustache](https://mustache.github.io/) 是一个无逻辑(logic-less)的模板引擎。源自于 google 的 [ctemplate](http://ctemplate.sourceforge.net/)。 ### 概要 一个示例 ```golang Hello {{name}} You have just won {{value}} dollars! {{#in_ca}} Well, {{taxed_value}} dollars, after taxes. {{/in_ca}} ``` 使用参数如下 ```json { "name": "Chris", "value": 10000, "taxed_value": 10000 - (10000 * 0.4), "in_ca": true } ``` 渲染输出如下 ```html Hello Chris You have just won 10000 dollars! Well, 6000.0 dollars, after taxes. ``` ### 简介 Mustache 可用于渲染 HTML,配置文件生成,源代码生成等所有场景。它的工作方式是使用 json 对象中的值替换/扩展模板中的 Tag。 之所以称之为“无逻辑”主要是因为 Mustache 不使用 if else 语句,不使用 for 循环。只有 Tag。有些 Tag 被替换为一个值,有的被替换为一系列值,有的则什么也不做,这取决于 Tag 的功能和渲染使用的参数。 ### Tag 类型 Tag 用双大括号来表示。 {{person}} 是一个 Tag,就像 {{#person}} 也是一个 Tag;这两者中,我们都是用 __person__ 作为 key,总共有以下几种 Tag. #### 变量 (Variables) 最基础的 tag 类型是变量类型。例如 {{name}} tag 是一个非常基本的变量类型,它会在当前的处理上下文中搜索 __name__ 这个 key,如果找不到,则会继续递归式地向上级上下文中搜索,如果直到顶级上下文也无法找到,则什么也不会渲染。 所有的变量类型默认都会转义 HTML 内容,如果想输出原始的 HTML 内容,需要使用三重大括号: {{{name}}}。 也可以使用 & 来表示非转义变量,例如 {{& name}},这在更改分隔符的情况下很有用。(详见 设置分隔符 部分) 通常,一个变量 tag 如果匹配不到会返回空字符串,这个特性可在各个 Mustache 实现库中更改(可配置)。例如 Ruby 版本的实现是在这种情况下抛出一个异常。 模板 ```golang * {{name}} * {{age}} * {{company}} * {{{company}}} ``` 参数: ```json { "name": "Chris", "company": "GitHub" } ``` 输出 ```html * Chris * * <b>GitHub</b> * GitHub ``` #### 段(Sections) 段类型渲染一个文本块一次或多次,取决于当前上下文中的对应 key 的值。段类型 Tag以 # 开始,以 / 结束。例如 {{#person}} 开始一个段 {{/person}} 结束。 段类型渲染行为取决于 key 的值,规则如下: 1. False 或 [] 如果 __person__ 这个 key 存在并且它的值是 false 或空列表,则段中的内容不会被渲染。 模板 ```golang Shown. {{#person}} Never shown! {{/person}} ``` 参数 ```json { "person": false } ``` 输出 ```html Shown. ``` 2. 非空列表 如果 __person__ 字段存在并且具有 non-false 值,则段中的内容会被渲染一次或多次。 如果值是非空列表,则段中的内容会使用列表中的每个元素渲染一次,每次渲染,当前元素就是它的上下文。可以使用段类型来处理集合。 模板 ```golang {{#repo}} {{name}} {{/repo}} ``` 参数 ```json { "repo": [ { "name": "resque" }, { "name": "hub" }, { "name": "rip" } ] } ``` 输出 ```html resque hub rip ``` 3. 函数 (Lambdas) 如果值是一个可调用对象,比如 function 或 lambda,则该对象会被调用,段中的内容会被作为参数以字面值的形式(不渲染)传入,{{tags}} 并不会被展开-一切交给 lambda。通过 lambda 可以实现过滤器和缓存。 模板 ```golang {{#wrapped}} {{name}} is awesome. {{/wrapped}} ``` 参数 ```json { "name": "Willy", "wrapped": function() { return function(text, render) { return "" + render(text) + "" } } } ``` 输出 ```html Willy is awesome. ``` 4. 非假值 如果值是一个非假值但不是一个列表,此值会被用作段内容的上下文进行一次渲染。 模板 ```golang {{#person?}} Hi {{name}}! {{/person?}} ``` 参数 ```json { "person?": { "name": "Jon" } } ``` 输出 ```html Hi Jon! ``` #### 倒置段 (Inverted Sections) 一个倒置段以 ^ 开始,以 / 结束。比如 {{^person}} 开始一个名为 __person__ 的倒置段,{{/person}} 结束。 段类型可以用来根据 key 的值来渲染段中内容一次或多次,倒置段类型则根据 key 的值来渲染段中内容最多一次: 当且仅当这个 key 不存在,值为 false 或为空列表。 模板 ```golang {{#repo}} {{name}} {{/repo}} {{^repo}} No repos :( {{/repo}} ``` 参数 ```json { "repo": [] } ``` 输出 ```html No repos :( ``` #### 注释 注释以感叹号开头,其中的内容会被忽略。比如模板 ```html

Today{{! ignore me }}.

``` 会被渲染为: ```html

Today.

``` 注释可以换行 #### 片段 (Partials) 片段以大于号开头,如 {{> box}}。 片段类型在运行时渲染(相对于编译时),所以递归式的片段类型是可行的,只是要注意避免无限循环问题。 片段类型会继承调用上下文。在一个 ERB(Ruby console 工具)文件中可能有如下代码: ```ruby <%= partial :next_more, :start => start, :size => size %> ``` 对应的 Mustache 则只需要如下: ```golang {{> next_more}} ``` 为什么呢?因为 next_more.mustache 文件会从调用上下文中继承 __size__ 和 __start__ 函数。 这会让人想起 include, import, template expansion, nested templates, 或者子模板,没有做不到,只有想不到。 来看一个例子: base.mustache 是一个模板文件,其中定义一个片段类型 tag,内容如下 ```golang

Names

{{#names}} {{> user}} {{/names}} ``` user.mustache 文件内容: ```golang {{name}} ``` 可以被看作一个文件,展开后如下: ```golang

Names

{{#names}} {{name}} {{/names}} ``` #### 设置分隔符 (Set Delimiter) 设置分隔符类型的 tag 以 = 开头,把 {{ 和 }} 组成的分隔符替换为自定义类型。 比如: ```golang * {{default_tags}} {{=<% %>=}} * <% erb_style_tags %> <%={{ }}=%> * {{ default_tags_again }} ``` 被渲染为一个三行的列表,第一行使用默认的 tag 分隔符,第二行使用设置分隔符类型 tag 指定的分隔符,第三行使用设置分隔符操作还原为默认的分隔符。 根据 [ctemplate](http://ctemplate.sourceforge.net/) 的描述,这“有益于处理那些双大括号可能随时出现从而让双大括号作为标记符难以使用的语言,比如 Tex” 最后,自定义的分隔符不能包含空格和 =。 ### 版权 Mustache is Copyright (C) 2009 Chris Wanstrath Original CTemplate by Google