likes
comments
collection
share

Mustache简介

作者站长头像
站长
· 阅读数 3

1.概述

在本文中,我们将关注Mustache模板,并使用它的一个JavaAPI来生成动态HTML内容。Mustache是一个无逻辑的模板引擎,用于创建动态内容,如HTML、配置文件等。

2.介绍

简单地说,引擎被归类为无逻辑的,因为它没有支持if-else语句和for循环的构造。Mustache模板由 {{}} 包围的标签名称组成(类似于胡子,因此得名)。

3.Maven依赖

模板的编译和执行可由多种语言支持,包括客户端和服务器端。为了能够处理Java中的模板,我们使用了它的Java库,该库可以作为Maven依赖项添加。 Java 8+:

<dependency>
    <groupId>com.github.spullara.mustache.java</groupId>
    <artifactId>compiler</artifactId>
    <version>0.9.4</version>
</dependency>

Java 6/7:

<dependency>
    <groupId>com.github.spullara.mustache.java</groupId>
    <artifactId>compiler</artifactId>
    <version>0.8.18</version>
</dependency>

4.用法

让我们看看一个简单的场景,它展示了如何:

  1. 编写一个简单的模板;
  2. 使用Java API编译模板;
  3. 通过提供必要的数据来执行。

4.1一个简单的Mustache模板

我们将创建一个简单的模板来显示待办事项任务的详细信息:

<h2>{{title}}</h2>
<small>Created on {{createdOn}}</small>
<p>{{text}}</p>

在上面的模板中,卷曲括号({{}})中的字段可以是:

  1. Java类的方法和属性;
  2. Map对象的key

4.2编译Mustache模板

我们可以编译如下所示的模板:

MustacheFactory mf = new DefaultMustacheFactory();
Mustache m = mf.compile("todo.mustache");

MustacheFactory在类路径中搜索给定的模板。在我们的示例中,我们将todo.mustache放在src/main/resources下。

4.3执行Mustache模板

提供给模板的数据将是Todo类的一个实例,其定义为:

public class Todo {
    private String title;
    private String text;
    private boolean done;
    private Date createdOn;
    private Date completedOn;
    
    // constructors, getters and setters
}

可以执行编译后的模板来获得HTML,如下所示:

Todo todo = new Todo("Todo 1", "Description");
StringWriter writer = new StringWriter();
m.execute(writer, todo).flush();
String html = writer.toString();

Mustache Sections and Iterations

现在让我们看看如何列出待办事项,为了迭代列表数据,我们使用Mustache sections。section是一个代码块,根据当前上下文中键的值重复一次或多次。 它看起来像:

{{#todo}}
<!-- Other code -->
{{/todo}}

节以(#)开始,以斜线(/)结束,其中这两个符号后面都跟着一个key,该键的value是渲染section的基础。 以下是根据key的值可能的情况:

5.1具有非空列表或非假值的Section

创建一个模板todo-section.mustache,它使用一个section:

{{#todo}}
<h2>{{title}}</h2>
<small>Created on {{createdOn}}</small>
<p>{{text}}</p>
{{/todo}}

让我们看看这个模板的作用:

@Test
public void givenTodoObject_whenGetHtml_thenSuccess() 
  throws IOException {
 
    Todo todo = new Todo("Todo 1", "Todo description");
    Mustache m = MustacheUtil.getMustacheFactory()
      .compile("todo.mustache");
    Map<String, Object> context = new HashMap<>();
    context.put("todo", todo);
 
    String expected = "<h2>Todo 1</h2>";
    assertThat(executeTemplate(m, todo)).contains(expected);
}

让我们创建另一个模板todos.mustache来列出todos

{{#todos}}
<h2>{{title}}</h2>
{{/todos}}

并使用它创建一个待办事项列表:

@Test
public void givenTodoList_whenGetHtml_thenSuccess() 
  throws IOException {
 
    Mustache m = MustacheUtil.getMustacheFactory()
      .compile("todos.mustache");
 
    List<Todo> todos = Arrays.asList(
      new Todo("Todo 1", "Todo description"),
      new Todo("Todo 2", "Todo description another"),
      new Todo("Todo 3", "Todo description another")
    );
    Map<String, Object> context = new HashMap<>();
    context.put("todos", todos);
 
    assertThat(executeTemplate(m, context))
      .contains("<h2>Todo 1</h2>")
      .contains("<h2>Todo 2</h2>")
      .contains("<h2>Todo 3</h2>");
}

5.2列表为空或值为False或Null的Section

让我们用一个null值测试todo-section.mustache:

@Test
public void givenNullTodoObject_whenGetHtml_thenEmptyHtml() 
  throws IOException {
    Mustache m = MustacheUtil.getMustacheFactory()
      .compile("todo-section.mustache");
    Map<String, Object> context = new HashMap<>();
    assertThat(executeTemplate(m, context)).isEmpty();
}

同样,用一个空list测试todos.mustache:

@Test
public void givenEmptyList_whenGetHtml_thenEmptyHtml() 
  throws IOException {
    Mustache m = MustacheUtil.getMustacheFactory()
      .compile("todos.mustache");
 
    Map<String, Object> context = new HashMap<>();
    assertThat(executeTemplate(m, context)).isEmpty();;
}

6.Inverted Sections

倒置Sections是指基于不存在的key、或者key为false或null值或空列表时,仅渲染一次的部分。换言之,倒置Sections在未渲染Sections时进行渲染。 它们以插入符号(^)开头,以斜线(/)结束,如下所示:

{{#todos}}
<h2>{{title}}</h2>
{{/todos}}
{{^todos}}
<p>No todos!</p>
{{/todos}}

以上模板在提供空list时:

@Test
public void givenEmptyList_whenGetHtmlUsingInvertedSection_thenHtml() 
  throws IOException {
 
    Mustache m = MustacheUtil.getMustacheFactory()
      .compile("todos-inverted-section.mustache");
  
    Map<String, Object> context = new HashMap<>();
    assertThat(executeTemplate(m, context).trim())
      .isEqualTo("<p>No todos!</p>");
}

7.Lambdas

mustache section的key的值可以是函数或lambda表达式。在这种情况下,通过将section中的文本作为参数传递给lambda表达式来调用完整的lambda表达式。

让我们来看一个模板todos-lambda.mustache:

{{#todos}}
<h2>{{title}}{{#handleDone}}{{doneSince}}{{/handleDone}}</h2>
{{/todos}}

handleDone的key解析为Java 8 lambda表达式,如下所示:

public Function<Object, Object> handleDone() {
    return (obj) -> done ? 
      String.format("<small>Done %s minutes ago<small>", obj) : "";
}

通过执行上述模板生成的HTML是:

<h2>Todo 1</h2>
<h2>Todo 2</h2>
<h2>Todo 3<small>Done 5 minutes ago<small></h2>

8.结论

在这篇介绍性文章中,我们研究了创建带有section、inverted section和Lambda的mustache模板。我们使用Java API通过提供相关数据来编译和执行模板。 Mustache的一些更高级的功能值得探索,例如:

  1. 提供一个callable作为value,从而产生并发评估;
  2. 使用DecoratedCollection获取集合元素的第一个、最后一个和索引;
  3. 反转API,在给定文本和模板的情况下提供数据。