一、初识Mustache
同样也是看Dropwizard才知道这个东西的,以前一直是知道诸如freemarker这样的模板引擎,这个是头一次听说,但是听周围的朋友说最早这个东西是出自于JS的,Dropwizard推荐使用这个东西,而且到mustache官网看了一下,发现十几种语言已经支持这个模板引擎技术,火热程度甚至超过了freemarker,看来到了不得不学的地步了,先来看看mustache是什么意思,我们都有一个不好的缺点,就是每次看到一个新鲜的东西就想知道他的中文名字叫什么,那么mustache的中文意思是什么?一定头疼?,它的英文是“胡子”,“胡须”的意思,好了不纠结这些东西了,我们先来了解一下mustache的基本知识,以及如何使用吧。
二、如何使用Mustache
2.1、名字
mustache官方给出的是Logic-less templates.翻译过来就是逻辑很少的模板,到底是不是呢?我们在学习的过程中慢慢去体会吧.
2.2、语法概述
2.2.1、一个比较典型的mustache模板如下所示
Hello {{name}}
You have just won {{value}} dollars!
{{#in_ca}}
Well, {{taxed_value}} dollars, after taxes.
{{/in_ca}}
2.2.2、给出的条件如下所示:
{
"name": "wangwenjun",
"value": 10000,
"taxed_value": 10000 - (10000 * 0.4),
"in_ca": true
}
2.2.3、那么得到的结果又会是怎样的呢?
<span style="background-color: rgb(0, 0, 0);"><span style="color:#006600;">Hello wangwenjun
You have just won 10000 dollars!
Well, 6000.0 dollars, after taxes.</span></span>
2.2.4、简单说明
2.3、Mustache的Tag类型详细介绍
Tag本地两对花括号标示出来,例如{{person}},当然{{#person}}也是一个Tag,这两个简单的例子中,我们使用了person作为tag的关键字,至于两种写法的区别在哪里,我们接下来慢慢的讨论。
2.3.1、变量
标签最主要的作用就是当作一个变量来使用,{{name}}标签在模板中会尝试查找name这个关键字在当前的上下文中,如果上下文中不存在name,父上下文将会通过递归的方式去查找,如果最*的上下文中依然找不到,name标签将不会被渲染,否则name标签会被替换渲染。
所有的变量在HTML中将会被过滤掉,如果你想返回没有经过转义的HTML元素,你可以使用三个花括号{{{name}}}.
当然你也可以使用&告诉上下文不要进行转义,如:{{&name}},这种方式非常有用,可以在下文中的设置分隔符中看到它的进一步用法,好了看一个简单的例子吧
mustache模板:
* {{name}}
* {{age}}
* {{company}}
* {{{company}}}
Hash 上下文:
{
"name": "Chris",
"company": "<b>GitHub</b>"
}
输出:
* Chris
*
* <b>GitHub</b>
* <b>GitHub</b>
我们一起分析一下模板的输出过程,其中第一个标签被替换为Chris,第二个标签中在整个上下文中不存在,因此不作渲染,第三个我们说过了会被转义,因为有html元素,最后一个如果你想保留html元素就使用三个花括号的形式,这样会被完整的输出。
2.3.2、块
{{#person}}同样也是一个标签,但是他的作用是块的意思,如果写成{{person}}那么就是一个变量,像前文所说的那样,在本节中,我们来学习一下块的用法,也就是标签的第二个类型
所谓块就是渲染一个区域的文本一次或者多次,当然需要依赖当前上下文中person所代表的内容,块的开始和结束是这样的形式
<span style="white-space:pre"> </span>{{#person}}同样一个块的所有行为也取决于person这个关键字在上下文中的值。
<span style="white-space:pre"> </span>balabalabala.
<span style="white-space:pre"> </span>{{/person}}
2.3.2.1 False和空的list
在上面的例子中,如果person这个key存在,并且有一个值是false或者一个空的列表,包含在块之间的元素不会做任何显示的.
模板代码:
Shown.
{{#person}}
Never shown!
{{/person}}
hash 上下文数据:
{
"person": false
}
输出:
Shown.
同样如果person是一个列表,如果它为空,标签内部的内容也是不会被显示出来的.
2.3.2.2 非空的列表和True
模板文件:
{{#repo}}
<b>{{name}}</b>
{{/repo}}
Hash 上下文数据:
{
"repo": [
{ "name": "resque" },
{ "name": "hub" },
{ "name": "rip" }
]
}
输出信息:
<b>resque</b>
<b>hub</b>
<b>rip</b>
2.3.2.3 Lambdas的使用
模板:wrapped是一个标签,准确的讲是一个section标签.
{{#wrapped}}
{{name}} is awesome.
{{/wrapped}}
Hash 上下文数据:
{
"name": "Willy",
"wrapped": function() {
return function(text) {
return "<b>" + text + "</b>"
}
}
}
输出:
<b>Willy is awesome.</b>
在这个例子中我们看到wrapped是一个可以被调用的函数他当标签使用的时候会被再次调用,并且包在其中的其他标签也会被转义执行,这个特性其实非常酷,可以用来做很多很多的事情.
2.3.2.4、非False的值
其实就是判断该key是否存在,如果存在则会执行section中的内容,不存在则不会执行.看一下下面的例子吧.
模板:
{{#person?}}
Hi {{name}}!
{{/person?}}
Hash 数据上下文:
{
"person?": { "name": "Jon" }
}
输出结果:
Hi Jon!
2.3.2.5、Inverted的块(section)
Inverted sections使用这样的格式 {{^person}}balabalabala{{/person}}有点类似于if
else这样的逻辑语句.
模板文件内容:
{{#repo}}
<b>{{name}}</b>
{{/repo}}
{{^repo}}
No repos :(
{{/repo}}
Hash 上下文:
{
"repo": []
}
输出结果为:
No repos :(
2.3.2.6、注释
良好的编码习惯,都会有言简意赅的注释作为辅佐,同样在mustache中存在注释的标签.我们来看看如何使用注释.<h1>Today{{! ignore me }}.</h1>
可以看到在key的前面加一个!操作符号就可以将其过滤不作显示.
<h1>Today.</h1>
2.3.2.7、Partials的使用
Partials 标签开始是以一个大于号开始,像这样{{> box}}
.
Partials在运行期被渲染 (相对于编译期渲染而言),因此可以使用它来做一些递归,可以避免无限的循环.
它也可以继承上下文的模板.你可以在wiki page [ERB](http://en.wikipedia.org/wiki/ERuby) 中看到如下的信息:
<%= partial :next_more, :start => start, :size => size %>
mustache则只需要如下短短几个字符就可以搞定:
{{> next_more}}
为什么呢? 因为 next_more.mustache
文件将会继承start和size.
这种方式你也许会联想到partials的作用相当于includes或者模板扩展,尽管这不是完全正确的,但是有时候的确可以这样认为.
举一个例子,教你如何使用mustache的特性(说真的,看官方文档,在这部分我起初没明白,最后仔细阅读,才理解它的意思):
base.mustache文件:
<h2>Names</h2>
{{#names}}
{{> user}}
{{/names}}
user.mustache文件:
<strong>{{name}}</strong>
上面是两个mustache的模板文件,在运行的过程中base.mustache使用了user.mustache,上面的效果等同于下面的一个模板文件,有时候我们这么写是为了让功能更集中,提高模板的重用率.
<h2>Names</h2>
{{#names}}
<strong>{{name}}</strong>
{{/names}}
2.3.2.8、设置分隔符号
有些时候我们的确是想修改一下mustache默认的标签分割符号{{}},但是值得庆幸的是,mustache允许我们这样做,而且方法非常简单,我们看一个例子吧
* {{default_tags}}
{{=<% %>=}}
* <% erb_style_tags %>
<%={{ }}=%>
* {{ default_tags_again }}
这里有三个标签,第一个采用默认的分隔符号,然后修改成<%%>这样的风格的风格符号,比关切输出标签内容,接下来又还原回{{}}这样的风格然后输出另外一个标签内容.
三、综合练习
好了,截止目前,关于mustache的用法基本上都已经介绍完毕了,最起码官方文档涵盖的东西我全部翻译了过来,应该没有什么遗漏的东西了,在本章节中我们来学习一下如何在Java中使用mustache,并且演示一个综合的应用.
3.1开发环境
IDEA:intellij IDEA
Maven :3.0.4
3.2 pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>websocket</artifactId>
<groupId>websocket</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mustache</artifactId>
<dependencies>
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>0.7.0</version>
</dependency>
</dependencies>
</project>
3.3 模板文件
{{#items}}
Name: {{name}}
Price: {{price}}
{{#features}}
Feature: {{description}}
{{/features}}
{{/items}}
3.4 java代码
package com.wangwenjun.mustache;
import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
public class MustacheExample {
public List<Item> items() {
return Arrays.asList(
new Item("Item 1", "$19.99", Arrays.asList(new Feature("New!"), new Feature("Awesome!"))),
new Item("Item 2", "$29.99", Arrays.asList(new Feature("Old."), new Feature("Ugly.")))
);
}
public static void main(String[] args) throws IOException {
MustacheFactory mf = new DefaultMustacheFactory();
Mustache mustache = mf.compile("template.mustache");
mustache.execute(new PrintWriter(System.out), new MustacheExample()).flush();
}
static class Feature {
private String description;
public Feature(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
public static class Item {
private String name, price;
private List<Feature> features;
public Item(String name, String price, List<Feature> features) {
this.name = name;
this.price = price;
this.features = features;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public List<Feature> getFeatures() {
return features;
}
public void setFeatures(List<Feature> features) {
this.features = features;
}
}
}
3.5 输出结果
Name:Item 1
Price:$19.99
New!
Awesome!
Name:Item 2
Price:$29.99
Old.
Ugly.
四、Mustache和Spring MVC的联合使用
很多热门的框架,Spring都会对其支持很好的支持,当然这不是spring官方发布的支持版本,而是在github上别人写的封装,我们知道spring mvc不仅提供了jsp,freemarker等视图技术,究其原因就是spring灵活的扩展性,可以很方便增多一种新的视图技术支持.
spring mustache github项目地址为:
https://github.com/sps/mustache-spring-view
pom
<dependency>
<groupId>com.github.sps.mustache</groupId>
<artifactId>mustache-spring-view</artifactId>
<version>1.3</version>
</dependency>
<!-- jmustache -->
<dependency>
<groupId>com.samskivert</groupId>
<artifactId>jmustache</artifactId>
<version>${jmustache.version}</version>
</dependency>
<!-- mustache.java -->
<dependency>
<groupId>com.github.spullara.mustache.java</groupId>
<artifactId>compiler</artifactId>
<version>${mustache.java.version}</version>
</dependency>
Spring 配置文件
<!-- jmustache -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.mustache.MustacheViewResolver">
<property name="suffix" value=""/>
<property name="cache" value="${TEMPLATE_CACHE_ENABLED}" />
<property name="templateFactory">
<bean class="org.springframework.web.servlet.view.mustache.jmustache.JMustacheTemplateFactory">
<property name="escapeHTML" value="true"/>
<property name="standardsMode" value="false"/>
<property name="templateLoader">
<bean class="org.springframework.web.servlet.view.mustache.jmustache.JMustacheTemplateLoader"/>
</property>
</bean>
</property>
</bean>
<!-- mustache.java -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.mustache.MustacheViewResolver">
<property name="suffix" value=""/>
<property name="cache" value="${TEMPLATE_CACHE_ENABLED}"/>
<property name="templateFactory">
<bean class="org.springframework.web.servlet.view.mustache.java.MustacheJTemplateFactory" />
</property>
</bean>