FreeMarker模板開發指南知識點梳理


freemarker是什么? 有什么用? 怎么用? (問得好,這些都是我想知道的問題)

 

freemarker是什么?

  FreeMarker 是一款 模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁,電子郵件,配置文件,源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。

對於像我一樣之前完全對freemarker沒概念的來說,這種解釋還是無法釋疑解惑,需要說的再詳盡點。

 

freemarker有什么用?

  模板編寫為FreeMarker Template Language (FTL)。它是簡單的,專用的語言。 那就意味着要准備數據在真實編程語言中來顯示,比如數據庫查詢和業務運算, 之后模板顯示已經准備好的數據。在模板中,你可以專注於如何展現數據, 而在模板之外可以專注於要展示什么數據。

  沒錯,這就是MVC模式。Java objects充當Controller角色,Template充當Model角色,Output顯然是View角色。看到熟悉的字眼Java、html標簽等等,是不是讓你聯想到另外一項技術——JSP,為了理解,你可以把freemarker和jsp拿來比較,只是JSP是一項技術也是一項官方標准,所以知名度會更高,兩項技術各有千秋,嚴格來說,JSP可以在文件里摻雜寫java代碼並不能稱得上真正的MVC模式。相比來說freemarker更為干凈純潔,性能也不錯。

官網http://freemarker.org/ 

 

 

freemarker怎么用?

其實所有內容都在freemarker手冊http://freemarker.org/docs/index.html

這里主要分為以下幾個部分:模板開發指南+程序開發指南+模板語言參考+XML處理指南+附錄

到目前為止,看了一遍模板開發指南,這里梳理一些主要知識點:

 

模板 + 數據模型 = 輸出

  場景:在某系統中的歡迎界面HTML頁面如下:

<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome John Doe!</h1>
  <p>Our latest product:
  <a href="products/greenmouse.html">green mouse</a>!
</body>
</html>

 

  freemarker思路:鑒於登錄系統的不同用戶會有不同的用戶名,這里不能一味寫死為John snow.這時候可以使用freemarker模板,加入一個指令如${user},這個user會在java中被賦值從而顯示在頁面上。這種處理方式更加具有MVC的思想,模板層不需要做任何改變,后台改變這個值,模板只是負責輸出。

  為模板准備的數據整體被稱作為 數據模型。 模板作者要關心的是,數據模型是樹形結構(就像硬盤上的文件夾和文件),在視覺效果上, 數據模型可以是:

(root)
  |
  +- user = "Big Joe"
  |
  +- latestProduct
      |
      +- url = "products/greenmouse.html"
      |
      +- name = "green mouse"

  

  有了數據模型和模板,就構成了freemarker的最終頁面輸出

  備注:這里的數據模型可以對比理解為頁面中的DOM結構,不同的數據有不同的DOM層級關系,對於數據模型的訪問也類似對於DOM元素的訪問。比如要訪問name這個數據(節點),需要攜程(root.)latestProduct.name。

 

模板指令

  模板可以使用一些基本指令如if指令

<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>
    Welcome ${user}<#if user == "Big Joe">, our beloved leader</#if>!
  </h1>
  <p>Our latest product:
  <a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>

  

  list指令

<#list misc.fruits>
  <ul>
    <#items as fruit>
      <li>${fruit}
    </#items>
  </ul>
</#list>

  list 指令的一般格式為: <#list sequence as loopVariable>repeatThis</#list>。

 

  include指令

  使用 include 指令, 我們可以在模板中插入其他文件的內容。

  假設要在一些頁面中顯示版權聲明的信息。那么可以創建一個文件來單獨包含這些版權聲明, 之后在需要它的地方插入即可。比方說,我們可以將版權信息單獨存放在頁面文件 copyright_footer.html 中:

<hr>
<i>
Copyright (c) 2000 <a href="http://www.acmee.com">Acmee Inc</a>,
<br>
All Rights Reserved.
</i>

  

  當需要用到這個文件時,可以使用 include 指令來插入:

<html>
<head>
  <title>Test page</title>
</head>
<body>
  <h1>Test page</h1>
  <p>Blah blah...
  <#include "/copyright_footer.html">
</body>
</html>

  

內建函數

內建函數很像子變量(如果了解Java術語的話,也可以說像方法), 它們並不是數據模型中的東西,是 FreeMarker 在數值上添加的。 為了清晰子變量是哪部分,使用 ?(問號)代替 .(點)來訪問它們。常用內建函數的示例:

  • user?html 給出 user 的HTML轉義版本, 比如 & 會由 &amp; 來代替。

  • user?upper_case 給出 user 值的大寫版本 (比如 "JOHN DOE" 來替代 "John Doe")

  • animal.name?cap_first 給出 animal.name 的首字母大寫版本(比如 "Mouse" 來替代 "mouse")

  • user?length 給出 user 值中 字符的數量(對於 "John Doe" 來說就是8)

  • animals?size 給出 animals 序列中 項目 的個數(我們示例數據模型中是3個)

  • 如果在 <#list animals as animal> 和對應的 </#list> 標簽中:

    • animal?index 給出了在 animals 中基於0開始的 animal的索引值

    • animal?counter 也像 index, 但是給出的是基於1的索引值

    • animal?item_parity 基於當前計數的奇偶性,給出字符串 "odd" 或 "even"。在給不同行着色時非常有用,比如在 <td class="${animal?item_parity}Row">中。

一些內建函數需要參數來指定行為,比如:

  • animal.protected?string("Y", "N") 基於 animal.protected 的布爾值來返回字符串 "Y" 或 "N"。

  • animal?item_cycle('lightRow','darkRow') 是之前介紹的 item_parity 更為常用的變體形式。

  • fruits?join(", ") 通過連接所有項,將列表轉換為字符串, 在每個項之間插入參數分隔符(比如 "orange,banana")

  • user?starts_with("J") 根據 user 的首字母是否是 "J" 返回布爾值true或false。

內建函數應用可以鏈式操作,比如user?upper_case?html 會先轉換用戶名到大寫形式,之后再進行HTML轉義。(這就像可以鏈式使用 .(點)一樣)

 

處理不存在的變量

  freemarker對於null的把關很嚴格,如果一個變量為null則會拋出異常,停止對於當前模板的編譯和顯示。所以對於不存在的變量的處理顯得格外重要,主要有兩種方式:

  1. 添加默認值設置

<h1>Welcome ${user!"visitor"}!</h1>

  這里表示當user變量如果不存在或為空的時候,就取默認值“visitor”顯示在頁面上,這里的“!”表示用於設置默認值

 

  2. 通過條件判斷

<#if user??><h1>Welcome ${user}!</h1></#if>

  這里表示如果users存在才顯示if指令里面的內容,否則跳過不執行。注意這里的"??"表示條件判斷的符號

 

數據類型

  freemarker支持的數據類型有:

    1.標量:字符串(如"hello")+數字(如123)+布爾值(如true,false)+日期/時間(如May 15,2016)

    2.容器:哈希表(類似java中的HashMap)+序列(類似數組)+集合

    3.子程序:方法和函數+用戶自定義指令

 

模板構成

  • 文本:文本會照着原樣來輸出。

  • 插值:這部分的輸出會被計算的值來替換。插值由 ${ and } 所分隔(或者 #{ and },這種風格已經不建議再使用了)。

  • FTL 標簽:FTL標簽和HTML標簽很相似,但是它們卻是給FreeMarker的指示, 而且不會打印在輸出內容中。

  • 注釋:注釋和HTML的注釋也很相似,但它們是由 <#-- 和 -->來分隔的。注釋會被FreeMarker直接忽略, 更不會在輸出內容中顯示。

   備注:FTL是區分大小寫的。

     插值 僅僅可以在 文本 中使用:

      用戶所犯的一個常見錯誤是將插值放在了不需要/不應該使用的地方。 插值  在文本區中有效。(比如, <h1>Hello ${name}!</h1>) 還有在字符串值中 (比如, <#include "/footer/${company}.html">)。 典型的 錯誤 使用是 <#if ${big}>...</#if>, 這會導致語法錯誤。簡單寫為 <#if big>...</#if>即可。 而且, <#if "${big}">...</#if> 也是 錯誤的, 因為它將參數值轉換為字符串,但是 if 指令只接受布爾值, 那么這將導致運行時錯誤。

       注釋 可以放在 FTL 標簽 和 插值中。

 

值域

  值域表達式的通用形式是( start 和 end 可以是任意的結果為數字表達式):

  • start..end: 包含結尾的值域。比如 1..4 就是 [1, 2, 3, 4]
  • start..<end 或 start..!end: 不包含結尾的值域。比如 1..<4 就是 [1, 2, 3]
  • start..*length: 限定長度的值域,比如 10..*4 就是 [10, 11, 12, 13]

 

freemarker也支持算數運算(+,-,*,/,%),比較運算(==,!=),邏輯運算(||,&&,!)等

 

自定義指令

  自定義指令可以使用 macro 指令來定義。

  宏是有一個變量名的模板片段。可以在模板中使用宏作為自定義指令, 這樣就能進行重復性的工作。例如,創建一個宏變量來輸出大字號的''Hello Joe!'':

<#macro greet>
  <font size="+2">Hello Joe!</font>
</#macro>

  

  macro 指令自身不輸出任何內容, 它只是用來創建宏變量,所以就會有一個名為 greet 的變量。在 <#macro greet> 和 </#macro> 之間的內容 (稱為 宏定義體) 將會在使用該變量作為指令時執行。可以在FTL標記中通過 @代替#來使用自定義指令。 使用變量名作為指令名。而且,自定義指令的 結束標記 也是需要的。那么, 就可以這樣來使用 greet

<@greet></@greet>

  或者<@greet/>

 

在模板中定義變量

在模板中可以定義三種類型的變量:

  • ''簡單''變量: 它能從模板中的任何位置來訪問,或者從使用 include 指令引入的模板訪問。可以使用 assign 指令來創建或替換這些變量。因為宏和方法只是變量,那么 macro 指令 和 function 指令 也可以用來設置變量,就像 assign 那樣。

  • 局部變量:它們只能被設置在 宏定義體內, 而且只在宏內可見。一個局部變量的生命周期只是宏的調用過程。可以使用 local指令 在宏定義體內創建或替換局部變量。

  • 循環變量:循環變量是由如 list 指令自動創建的,而且它們只在指令的開始和結束標記內有效。宏 的參數是局部變量而不是循環變量。

  • 全局變量:這是一個高級話題了, 並且這種變量最好別用。即便它們屬於不同的命名空間, 全局變量也被所有模板共享,因為它們是被 import進來的, 不同於 include 進來的。那么它們的可見度就像數據模型那樣。 全局變量通過 global指令來定義。

  備注:局部變量也會隱藏(不是覆蓋)同名的''簡單''變量。 循環變量也會隱藏(不是覆蓋)同名的''簡單''變量。例如:

<#assign x = "plain">
1. ${x}  <#-- we see the plain var. here -->
<@test/>
6. ${x}  <#-- the value of plain var. was not changed -->
<#list ["loop"] as x>
    7. ${x}  <#-- now the loop var. hides the plain var. -->
    <#assign x = "plain2"> <#-- replace the plain var, hiding does not mater here -->
    8. ${x}  <#-- it still hides the plain var. -->
</#list>
9. ${x}  <#-- the new value of plain var. -->

<#macro test>
  2. ${x}  <#-- we still see the plain var. here -->
  <#local x = "local">
  3. ${x}  <#-- now the local var. hides it -->
  <#list ["loop"] as x>
    4. ${x}  <#-- now the loop var. hides the local var. -->
  </#list>
  5. ${x}  <#-- now we see the local var. again -->
</#macro>

  

  輸出為:

1. plain
  2. plain
  3. local
    4. loop
  5. local
6. plain
    7. loop
    8. loop
9. plain2

  

命名空間

  如果想創建可以重復使用的宏,函數和其他變量的集合, 通常用術語來說就是引用 。 使用多個命名空間是必然的。只要考慮你在一些項目中, 或者想和他人共享使用的時候,你是否有一個很大的宏的集合。 但要確保庫中沒有宏(或其他變量)名和數據模型中變量同名, 而且也不能和模板中引用其他庫中的變量同名是不可能的。 通常來說,變量因為名稱沖突時也會相互沖突。 所以要為每個庫中的變量使用不同的命名空間。

  我們來建立一個簡單的庫。假設你需要通用的變量 copyright 和 mail (在你有疑問之前,宏 當作是 變量):

<#macro copyright date>
  <p>Copyright (C) ${date} Julia Smith. All rights reserved.</p>
</#macro>

<#assign mail = "jsmith@acme.com">

 

   把上面的這些定義存儲在文件 lib/my_test.ftl 中 (目錄是存放模板的位置)。假設想在 aWebPage.ftl 中使用這個模板。如果在aWebPage.ftl 中使用 <#include "/lib/my_test.ftl">, 那么就會在主命名空間中創建兩個變量,這樣就不是很好, 因為想讓它們只在同一個命名空間''My Test Library''中。所以就不得不使用 import指令 來代替 include 了。乍一看,這個指令和 include 很相似,但是它會為 lib/my_test.ftl 創建一個空的命名空間,然后在那里執行。lib/my_test.ftl 會發現它自己在一個新的環境中,那里只有數據模型的變量可以找到 (因為它們在哪兒都是可見的),然后會在這個環境中創建兩個變量。現在來看這很不錯, 但是如果想訪問aWebPage.ftl 中的兩個變量, 而它們使用的是主命名空間,就不能看到其他命名空間中的變量。 解決方法是 import 指令不僅僅創建命名空間,而且要通過 import 的調用者(本例中的主命名空間)創建一個新的哈希表變量, 這就成為進入新的命名空間的大門。那么aWebPage.ftl 就像下面這樣:

<#import "/lib/my_test.ftl" as my> <#-- the hash called "my" will be the "gate" -->
<@my.copyright date="1999-2002"/>
${my.mail}

  

至此,梳理了關於模板開發指南部分的知識點。

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,並和您一起分享我日常閱讀過的優質文章。

  

友情贊助

如果你覺得博主的文章對你那么一點小幫助,恰巧你又有想打賞博主的小沖動,那么事不宜遲,趕緊掃一掃,小額地贊助下,攢個奶粉錢,也是讓博主有動力繼續努力,寫出更好的文章^^。

    1. 支付寶                          2. 微信

                      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM