Mustache模板技术,一个比freemarker轻量级的模板引擎

初识Mustache

同样也是看Dropwizard才知道这个东西的,以前一直是知道诸如freemarker这样的模板引擎,这个是头一次听说,但是听周围的朋友说最早这个东西是出自于JS的,Dropwizard推荐使用这个东西,而且到mustache官网看了一下,发现十几种语言已经支持这个模板引擎技术,火热程度甚至超过了freemarker,看来到了不得不学的地步了,
先来看看mustache是什么意思,我们都有一个不好的缺点,就是每次看到一个新鲜的东西就想知道他的中文名字叫什么,那么mustache的中文意思是什么?一定头疼?大笑,它的英文是“胡子”,“胡须”的意思,好了不纠结这些东西了,我们先来了解一下mustache的基本知识,以及如何使用吧。

如何使用Mustache

名字

mustache官方给出的是Logic-less templates.翻译过来就是逻辑很少的模板,到底是不是呢?我们在学习的过程中慢慢去体会吧.

语法概述

一个比较典型的mustache模板如下所示

[javascript] view plain copy
Hello {{name}}
You have just won {{value}} dollars!
{{#in_ca}}
Well, {{taxed_value}} dollars, after taxes.
{{/in_ca}}

给出的条件如下所示:

[javascript] view plain copy
{
“name”: “wangwenjun”,
“value”: 10000,
“taxed_value”: 10000 - (10000 * 0.4),
“in_ca”: true
}

那么得到的结果又会是怎样的呢?

[html] view plain copy
Hello wangwenjun
You have just won 10000 dollars!
Well, 6000.0 dollars, after taxes.

简单说明

Mustcache可以被用于html文件,配置文件,源代码等等很多场景,它的运行得益于扩展一些标签在模板文件中,然后使用一个hash字典或者对象对其进行替换渲染。我们之所以称之为“logic-less”是因为他摒弃了if else 以及for loop 这样的语句,取而代之的是只有标签,有些标签被替换成一个值,或者不作为,或者是一系列的值,比如数组或者一个列表,标签有几种不同的类型,自接下来我们会逐个介绍,但是你要相信他非常简单,因为他是“logic-less”的

Mustache的Tag类型详细介绍

Tag本地两对花括号标示出来,例如{{person}},当然{{#person}}也是一个Tag,这两个简单的例子中,我们使用了person作为tag的关键字,至于两种写法的区别在哪里,我们接下来慢慢的讨论。

变量

标签最主要的作用就是当作一个变量来使用,{{name}}标签在模板中会尝试查找name这个关键字在当前的上下文中,如果上下文中不存在name,父上下文将会通过递归的方式去查找,如果最顶级的上下文中依然找不到,name标签将不会被渲染,否则name标签会被替换渲染。

所有的变量在HTML中将会被过滤掉,如果你想返回没有经过转义的HTML元素,你可以使用三个花括号{{{name}}}.

当然你也可以使用&告诉上下文不要进行转义,如:{{&name}},这种方式非常有用,可以在下文中的设置分隔符中看到它的进一步用法,好了看一个简单的例子吧

mustache模板:

  • {{name}}
  • {{age}}
  • {{company}}
  • {{{company}}}
    Hash 上下文:

{
“name”: “Chris”,
“company”: “GitHub
}
输出:

  • Chris
    *
  • <b>GitHub</b>
  • GitHub
    我们一起分析一下模板的输出过程,其中第一个标签被替换为Chris,第二个标签中在整个上下文中不存在,因此不作渲染,第三个我们说过了会被转义,因为有html元素,最后一个如果你想保留html元素就使用三个花括号的形式,这样会被完整的输出。

{{#person}}同样也是一个标签,但是他的作用是块的意思,如果写成{{person}}那么就是一个变量,像前文所说的那样,在本节中,我们来学习一下块的用法,也就是标签的第二个类型

所谓块就是渲染一个区域的文本一次或者多次,当然需要依赖当前上下文中person所代表的内容,块的开始和结束是这样的形式

[html] view plain copy
{{#person}}
balabalabala.
{{/person}}
同样一个块的所有行为也取决于person这个关键字在上下文中的值。

False和空的list

在上面的例子中,如果person这个key存在,并且有一个值是false或者一个空的列表,包含在块之间的元素不会做任何显示的.

模板代码:

Shown.
{{#person}}
Never shown!
{{/person}}
hash 上下文数据:

{
“person”: false
}
输出:

Shown.
同样如果person是一个列表,如果它为空,标签内部的内容也是不会被显示出来的.

非空的列表和True

如果person这个key存在并且是一个非false的值,或者他是一个非空的列表元素,那么包括在标签内部的元素会被显示出来,当person只是一个boolean值的时候会显示一行数据,当person是一个列表数据类型的时候会循环显示,看看下面的例子吧
模板文件:

{{#repo}}
{{name}}
{{/repo}}
Hash 上下文数据:

{
“repo”: [
{ “name”: “resque” },
{ “name”: “hub” },
{ “name”: “rip” }
]
}
输出信息:

resque
hub
rip

Lambdas的使用

当Key的值是一个可以被调用的对象,譬如是一个函数或者一个lambda,该对象将会被调用并且传递标签包含的文本进去,我们来看看下面的这个例子
模板: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是一个可以被调用的函数他当标签使用的时候会被再次调用,并且包在其中的其他标签也会被转义执行,这个特性其实非常酷,可以用来做很多很多的事情.

非False的值

其实就是判断该key是否存在,如果存在则会执行section中的内容,不存在则不会执行.看一下下面的例子吧.

模板:

{{#person?}}
  Hi {{name}}!
{{/person?}}

Hash 数据上下文:

{
  "person?": { "name": "Jon" }
}

输出结果:

Hi Jon!

Inverted的块(section)

Inverted sections使用这样的格式 {{^person}}balabalabala{{/person}}有点类似于if else这样的逻辑语句.

模板文件内容:

{{#repo}}
  <b>{{name}}</b>
{{/repo}}
{{^repo}}
  No repos :(
{{/repo}}

Hash 上下文:

{
  "repo": []
}

输出结果为:

No repos :(

注释

良好的编码习惯,都会有言简意赅的注释作为辅佐,同样在mustache中存在注释的标签.我们来看看如何使用注释.

<h1>Today{{! ignore me }}.</h1>

可以看到在key的前面加一个!操作符号就可以将其过滤不作显示.

<h1>Today.</h1>

Partials的使用

Partials 标签开始是以一个大于号开始,像这样{{> box}}.

Partials在运行期被渲染 (相对于编译期渲染而言),因此可以使用它来做一些递归,可以避免无限的循环.

它也可以继承上下文的模板.你可以在wiki page ERB 中看到如下的信息:

<%= 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}}

设置分隔符号

有些时候我们的确是想修改一下mustache默认的标签分割符号{{}},但是值得庆幸的是,mustache允许我们这样做,而且方法非常简单,我们看一个例子吧

* {{default_tags}}
{{=<% %>=}}
* <% erb_style_tags %>
<%={{ }}=%>
* {{ default_tags_again }}

这里有三个标签,第一个采用默认的分隔符号,然后修改成<%%>这样的风格的风格符号,比关切输出标签内容,接下来又还原回{{}}这样的风格然后输出另外一个标签内容.

综合练习

好了,截止目前,关于mustache的用法基本上都已经介绍完毕了,最起码官方文档涵盖的东西我全部翻译了过来,应该没有什么遗漏的东西了,在本章节中我们来学习一下如何在Java中使用mustache,并且演示一个综合的应用.

开发环境

IDEA:intellij IDEA

Maven :3.0.4

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>  

模板文件

{{#items}}  
    Name: {{name}}  
    Price: {{price}}  
      {{#features}}  
            Feature: {{description}}  
      {{/features}}  
{{/items}}  

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;  
        }  
    }  
}  

输出结果

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>  

猜你喜欢

转载自blog.csdn.net/cnhome/article/details/80516735