1. 程式人生 > >Mustache模板技術,一個比freemarker輕量級的模板引擎

Mustache模板技術,一個比freemarker輕量級的模板引擎

一、初識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、簡單說明

Mustcache可以被用於html檔案,配置檔案,原始碼等等很多場景,它的執行得益於擴充套件一些標籤在模板檔案中,然後使用一個hash字典或者物件對其進行替換渲染。我們之所以稱之為“logic-less”是因為他摒棄了if else 以及for loop 這樣的語句,取而代之的是隻有標籤,有些標籤被替換成一個值,或者不作為,或者是一系列的值,比如陣列或者一個列表,標籤有幾種不同的型別,自接下來我們會逐個介紹,但是你要相信他非常簡單,因為他是“logic-less”的

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
*
* &lt;b&gt;GitHub&lt;/b&gt;
* <b>GitHub</b>
我們一起分析一下模板的輸出過程,其中第一個標籤被替換為Chris,第二個標籤中在整個上下文中不存在,因此不作渲染,第三個我們說過了會被轉義,因為有html元素,最後一個如果你想保留html元素就使用三個花括號的形式,這樣會被完整的輸出。

2.3.2、塊

{{#person}}同樣也是一個標籤,但是他的作用是塊的意思,如果寫成{{person}}那麼就是一個變數,像前文所說的那樣,在本節中,我們來學習一下塊的用法,也就是標籤的第二個型別

所謂塊就是渲染一個區域的文字一次或者多次,當然需要依賴當前上下文中person所代表的內容,塊的開始和結束是這樣的形式

<span style="white-space:pre">	</span>{{#person}}
   	<span style="white-space:pre">	</span>balabalabala.
<span style="white-space:pre">	</span>{{/person}}	
同樣一個塊的所有行為也取決於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

如果person這個key存在並且是一個非false的值,或者他是一個非空的列表元素,那麼包括在標籤內部的元素會被顯示出來,當person只是一個boolean值的時候會顯示一行資料,當person是一個列表資料型別的時候會迴圈顯示,看看下面的例子吧

模板檔案:

{{#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的使用

當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是一個可以被呼叫的函式他當標籤使用的時候會被再次呼叫,並且包在其中的其他標籤也會被轉義執行,這個特性其實非常酷,可以用來做很多很多的事情.

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專案地址為:

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>