關(guān)于Thymeleaf的真相

Thymeleaf 一直以來都是個(gè)使用小眾的模板引擎余佃,在2.0以前,最為人吐槽的是性能跌到無底線藐唠。甚至朋友的項(xiàng)目因?yàn)門hymeleaf性能慢帆疟,影響到整個(gè)項(xiàng)目慢孵滞。Stackoveflow 社區(qū)也有很多人吐槽影響了自己的系統(tǒng)性能。
總所周知鸯匹,系統(tǒng)單純某一方面性能慢很難影響整個(gè)系統(tǒng)性能坊饶,如果系統(tǒng)慢,最有可能的是數(shù)據(jù)存取出問題殴蓬,然而Thymeleaf能導(dǎo)致系統(tǒng)奇慢無比匿级,確實(shí)是開源軟件頭一遭。
如果你不相信Thymeleaf2.0性能慢染厅,可以參考 mbosecke/template-benchmark,或者國內(nèi)的一個(gè)評(píng)測(cè) https://my.oschina.net/smile622/blog/339884 (順便提一下痘绎,這倆個(gè)性能基準(zhǔn)測(cè)試,beetl都是最高的)

[圖片上傳失敗...(image-171e2f-1512133068978)]

據(jù)說Thymeleaf3.0性能成倍的提高了肖粮,在我用上面的基準(zhǔn)測(cè)試匯總孤页,Thymeleaf3.0仍然比最慢的Freemaker還慢很多。希望3.0不會(huì)影響到系統(tǒng)性能

輸入圖片說明

關(guān)于性能這一塊涩馆,并不是我打算揭示的真相行施,畢竟這一塊,早有定論魂那。我要揭示的Thymeleaf真相蛾号,是它宣稱的瀏覽器能直接打開,適合前端開發(fā)這個(gè)特點(diǎn)涯雅,還有他宣稱所謂的優(yōu)雅語法,以及謠言它是Spring Boot 默認(rèn)模板引擎

瀏覽器直接能打開模板鲜结?

Thymeleaf 網(wǎng)站首頁重點(diǎn)介紹的是使用Thymeleaf編寫的模板能直接用瀏覽器打開,所以適合前端開發(fā)使用活逆。這是因果關(guān)系精刷。可我認(rèn)為蔗候,這個(gè)因也錯(cuò)了怒允,果也錯(cuò)了。

Thymeleaf編寫的模板并不是所有都可以用瀏覽器打開琴庵,簡(jiǎn)單的模板頁面可以误算。這點(diǎn)Beetl也能做到仰美。復(fù)雜的模板頁面迷殿,Thymeleaf編寫的模板用瀏覽器打開照樣沒有效果。

Thymeleaf官網(wǎng)首頁忽悠出來的第一個(gè)例子舉例子吧

<table>
  <thead>
    <tr>
      <th th:text="#{msgs.headers.name}">Name</th>
      <th th:text="#{msgs.headers.price}">Price</th>
    </tr>
  </thead>
  <tbody>
    <tr th:each="prod: ${allProducts}">
      <td th:text="${prod.name}">Oranges</td>
      <td th:text="${#numbers.formatDecimal(prod.price, 1, 2)}">0.99</td>
    </tr>
  </tbody>
</table>

這個(gè)模板給人的感覺似乎確實(shí)能讓瀏覽器打開咖杂。因?yàn)樗褂昧藅h:text 屬性庆寺,瀏覽器會(huì)忽略這個(gè)屬性。所以瀏覽器打開诉字,看的像一個(gè)靜態(tài)的html頁面懦尝。

如果所有的模板頁面都是這么簡(jiǎn)單知纷,那么beetl也能做到這點(diǎn),比如定義beetl的定界符為

<!--:    -->

類似beetl的模板是這樣

<table>
  <thead>
    <tr>
      <th >${local("headers.name")}</th>
      <th >${local("headers.price")}</th>
    </tr>
  </thead>
  <tbody>
    <!--: for(prod in allProducts){ -->
    <tr >
      <td >${prod.name}</td>
      <td >${prod.price,".##"}</td>
    </tr>
  <!--: } -->
  </tbody>
</table>

這段beetl改寫的代碼照樣能用瀏覽器打開陵霉,你也許會(huì)認(rèn)為琅轧,beetl模板打開后出現(xiàn)了占位符,貌似比Thymeleaf差一點(diǎn)踊挠,但我卻認(rèn)為乍桂,如果瀏覽器打開Thymeleaf模板的都是靜態(tài)文本,你都不清楚哪兒是靜態(tài)文本效床,哪兒是動(dòng)態(tài)文本需要關(guān)注睹酌,這點(diǎn)還真不如Beetl更適合。

Beetl當(dāng)然不是為了所謂能瀏覽器打開的模板設(shè)計(jì)的模板語言剩檀,只是盡力做到了瀏覽器能打開憋沿,比如Beetl的include標(biāo)簽實(shí)際上就努力嘗試在重用模板的時(shí)候,又可以盡量讓前端人員理解include內(nèi)容沪猴,比如

<!--: include("/common/header.btl"){ -->
<script src="common.js" />
<script src="ext.js" />
<!--: } -->

這就是內(nèi)置的include辐啄,為什么會(huì)帶有{},從beetl設(shè)計(jì)剛開始的愿景就是能讓前端團(tuán)隊(duì)盡量理解模板引擎运嗜。
然而则披,我知道,光靠瀏覽器能打開并不能做到這一點(diǎn)洗出,況且士复,我認(rèn)為Thymeleaf在這個(gè)功能宣傳上過于夸大,并不能做到翩活。

比如阱洪,還是上面這個(gè)例子,我需要根據(jù)條件判斷以確定<td></td> 里顯示的內(nèi)容菠镇,那么我可能這么寫

  <tr>
      <th th:text="#{msgs.headers.name}">Name</th>
      <th th:text="#{msgs.headers.price}">Price</th>
    </tr>
  </thead>
  <tbody>
    <tr th:each="prod: ${allProducts}">
        <td th:text="'****'"  >Oranges</td>
       <td th:text="${prod.name}" th:if="${admin==true}>Oranges</td>
      <td th:text="${#numbers.formatDecimal(prod.price, 1, 2)}">0.99</td>
    </tr>
  </tbody>

上述增加了一個(gè)if語句冗荸,比如admin情況下,可以顯示名稱利耍。這樣蚌本,一個(gè)tr下會(huì)出現(xiàn)三個(gè)td,這顯然與th不對(duì)應(yīng)隘梨,這樣的Thymeleaf會(huì)顯示成什么樣呢程癌?顯然不是Thymeleaf所宣稱的那個(gè)樣子

再以Thymeleaf官網(wǎng)文檔switch例子說明,如下一個(gè)switch語句用法轴猎,你真希望在瀏覽器顯示3個(gè)段落嗎嵌莉?顯然不是。

<div th:switch="${user.role}">
  <p th:case="'admin'">User is an administrator</p>
  <p th:case="#{roles.manager}">User is a manager</p>
  <p th:case="*">User is some other thing</p>
</div>

盡管Thymeleaf做了最大的努力捻脖,但遇到復(fù)雜的模板锐峭,我的意思是中鼠,在我們項(xiàng)目,哪怕最簡(jiǎn)單的一個(gè)項(xiàng)目沿癞,Thymeleaf模板都不可能實(shí)現(xiàn)他所謂的瀏覽器打開援雇,這是個(gè)美好的愿望,Beetl 從7年前開始研發(fā)哪一天也有這樣的愿望椎扬,但我深知是做不到的熊杨,Thymeleaf在努力做,花費(fèi)了巨大的代價(jià)(龐大的奇怪的語法盗舰,會(huì)在后面提到)晶府,但不可能做到。

瀏覽器能預(yù)覽模板又能怎么樣钻趋?

Thymeleaf一直宣傳的優(yōu)勢(shì)是能瀏覽器打開模板川陆,前面已經(jīng)說道,不可能實(shí)現(xiàn)蛮位。那么問題來了较沪,就算簡(jiǎn)單的模板,瀏覽器打開了失仁,對(duì)前端開發(fā)人員有多大意義(這里潛臺(tái)詞是前后分離方案)

我在Thymeleaf官網(wǎng)沒有看到Thymeleaf對(duì)前后端分離的具體措施尸曼,也沒有看到互聯(lián)網(wǎng)上任何文檔說明如何使用Thymeleaf做前后分離,反到是Beetl萄焦,提供了充分的前后端分離方案控轿。

作為前端人員,如果他真的用Thymeleaf編寫模板拂封,他最大的需要并不是Thymeleaf反復(fù)提及的能在瀏覽器打開模板就行茬射,這并不能保證模板是萬無一失,可以放心交給后端人員集成了冒签。他最需要的是能模擬后端各種數(shù)據(jù)在抛,對(duì)模板做各種測(cè)試,好像模板真的經(jīng)過后端渲染一樣萧恕。這才是分離開發(fā)刚梭,這才前端人員真正需要的。
Beetl才能真正做到這一點(diǎn)票唆,Beetl使用前端人員熟悉的JS語法和HTML擴(kuò)展標(biāo)簽編寫模板朴读,Beetl的WebSimulate 插件能讓前端人員去模擬后臺(tái)數(shù)據(jù),模擬出各種數(shù)據(jù)惰说,各種分支情況磨德,像真的后臺(tái)調(diào)用一樣缘回。這時(shí)候吆视,前端人員典挑,打開瀏覽器訪問,這才是真正的終極“瀏覽器打開模板”啦吧,而不是Thymeleaf那種假的“瀏覽器能預(yù)覽模板”

Thymeleaf為這種假的“瀏覽器能預(yù)覽模板” 付出了巨大的語法代價(jià)您觉,如果你瀏覽Thymeleaf技術(shù)文檔,你會(huì)發(fā)現(xiàn)授滓,不可能讓前端人員掌握Thymeleaf語法琳水,你看看我的一個(gè)Thymeleaf部分語法貼圖你就知道了


![![輸入圖片說明](http://upload-images.jianshu.io/upload_images/3589798-4c87976b1a7ff163.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 "在這里輸入圖片標(biāo)題")](https://static.oschina.net/uploads/img/201708/09224916_VhrM.png "在這里輸入圖片標(biāo)題")
![![輸入圖片說明](http://upload-images.jianshu.io/upload_images/3589798-4c87976b1a7ff163.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240 "在這里輸入圖片標(biāo)題")](https://static.oschina.net/uploads/img/201708/09224916_VhrM.png "在這里輸入圖片標(biāo)題")

這部分語法只占Thymeleaf整體語法的5-10%。Thymeleaf的推崇者般堆,你們確定以及肯定前端人員會(huì)去使用嗎在孝?Beetl不僅僅能前段后端分離,語法比這少多了淮摔,而且私沮,都是前端人員熟悉的類似JS的語法。孰優(yōu)孰劣和橙,一看就知道仔燕。

所謂Thymeleaf的優(yōu)雅語法

國內(nèi)的Thymeleaf推崇者認(rèn)為它的語法優(yōu)雅。恕我直言魔招,我實(shí)在欣賞不來Thymeleaf語法晰搀,我會(huì)列舉出官網(wǎng)文檔的語法例子,各位看官憑直覺看一看

Variable Expressions: ${...}
Selection Variable Expressions: *{...}
Message Expressions: #{...}
Link URL Expressions: @{...}
Fragment Expressions: ~{...}

以上表達(dá)式共同特點(diǎn)都是有{}符號(hào)办斑,但不同的是前面有不同的符號(hào)外恕,表示不同的意思,我這里懶得解釋乡翅,因?yàn)門hymeleaf我也是新手..... 不小心非常容易出錯(cuò)吁讨,Beetl則只有一個(gè)標(biāo)準(zhǔn)的${} 引用。

如果你認(rèn)為{} 符號(hào)很明確的話峦朗,那你就大錯(cuò)特錯(cuò)了建丧,

{}
{{}}

{{}} 又完全是另外一個(gè)意思,這不符合程序員直覺波势,因?yàn)樗姓Z言里翎朱,{} 和 {{}} 效果是一樣的,Thymeleaf卻表達(dá)了不同的含義尺铣。

Thymeleaf顯然喜歡組合不同的符號(hào)來完成特定的模板渲染拴曲,其他模板語言則通常使用更為常見的函數(shù)調(diào)用,格式化函數(shù)來完成凛忿。一個(gè)模板渲染引擎澈灼,真沒有必要搞那么復(fù)雜語法。

如果這只是惡心到你,但并沒有讓你混亂叁熔,那我們接著看看Thymeleaf更多的語法

<div th:if="${user.isAdmin()} == false"> 

<div th:if="${user.isAdmin() == false}"> ..

這倆種是不同寫法委乌,但是等價(jià)的,前提是前者使OGNL引擎荣回,后者使用了SpringEL引擎遭贸。在Spring上下文里,Thymeleaf的表現(xiàn)有很大不同心软,所以你要非常清楚壕吹,不同的引擎,Thymeleaf應(yīng)該怎么去寫删铃。

你也許有點(diǎn)混亂了耳贬,不知道下面的寫法是不是更為混亂(都是官網(wǎng)的例子)

<span th:text="${onevar} + ' ' + |${twovar}, ${twovar}|">

<div th:with="isEven=(${prodStat.count} % 2 == 0)">

第一段如果你沒看懂它的意思,我也不會(huì)解釋猎唁。 至于第二段代碼效拭,意義較為明確,但如果我想擴(kuò)展這個(gè)表達(dá)式胖秒,增加一個(gè)變量缎患,那我應(yīng)該是下面那種寫法呢?

<div th:with="isEven=(${prodStat.count+rate} % 2 == 0)">

或者

<div th:with="isEven=( (${prodStat.count}+${rate} )% 2 == 0)">

我并不清楚阎肝,Thymeleaf怎么寫挤渔,更體會(huì)不到Thymeleaf的優(yōu)雅了。
如果是beetl模板风题,就很明確判导,就是一個(gè)類似js的表達(dá)式,在占位符里${ } 隨意書寫

<div style= "${ (prodStat.count+rate)%2==0?"event":"odd" } >

如你所見沛硅,Thymeleaf語法體系非逞廴校混亂,而且語法龐大摇肌,我上面的那個(gè)貼圖不過Thymeleaf的語法5%左右擂红,還有更多的Thymeleaf等待那些所謂的推崇者去挖掘,去寫博客介紹围小,去介紹“回字的四種寫法”(來自魯迅的《孔乙己》)

Beetl很少有使用者寫博客介紹Beetl昵骤,這是因?yàn)锽eetl足夠簡(jiǎn)單,普通功能真沒有寫博客介紹的必要肯适。

Spring Boot 所謂內(nèi)置Thymeleaf

國內(nèi)有些Thymeleaf推崇者試圖以Spring Boot 所謂內(nèi)置Thymeleaf來表明使用Thymeleaf是一條很正確的道路变秦。實(shí)際上并不是這樣

首先,Spring Boot 每一類型技術(shù)框舔,都會(huì)集成幾種技術(shù)蹦玫,比如Cache, 集成了自家的Redis赎婚,也有EhCache,Hazelcast樱溉,Spring Boot 并沒有明確說出來挣输,Spring Boot 推薦使用哪種技術(shù)。Spring Boot提供了多種選擇饺窿。這非常公平

其次歧焦,Spring Boot 默認(rèn)配置并不是代表最好移斩。以REST Template為例子肚医,Spring Boot默認(rèn)使用的是JDK URL Connection,這難道比其他可選的HTTPClient向瓷,OKHttp更好嗎肠套?顯然不是,還有Spring Boot提供的數(shù)據(jù)庫連接池猖任,是Tomcat的一款你稚,這難道會(huì)比Druid,HikariCP更好播瞳,顯然也不是

最后坏平,Spring Boot為什么會(huì)在模板引擎里集成除了Freemaker贬蛙,Groovy外,還會(huì)集成Thymeleaf呢宇弛,我想最大的原因是Thymeleaf深度使用了Spring技術(shù),比如上一節(jié)提到的Spring SpEL(相當(dāng)于其核心使用了SpEL)源请,還有未提到枪芒,Thymeleaf官網(wǎng)說的Conversion Service 。正是因?yàn)檫@種如此深度集成谁尸,才使得Spring Boot 會(huì)選擇Thymeleaf作為其中的一個(gè)模板引擎候選舅踪。而Velocity作為Apache體系技術(shù),且7年不維護(hù)(恰好今天2.0發(fā)布了)良蛮,當(dāng)然不會(huì)作為候選抽碌。

Beetl是我7年前開始研發(fā)的模板語言,廣泛在國內(nèi)使用决瞳,客戶有一流的互聯(lián)網(wǎng)公司咬展,國內(nèi)大型企業(yè),也有小型的創(chuàng)業(yè)公司瞒斩,個(gè)人用戶破婆。Beetl是真正經(jīng)得起考驗(yàn)的技術(shù)。很長時(shí)間來胸囱,我在模板引擎領(lǐng)域積累的經(jīng)驗(yàn)足以使我對(duì)Thymeleaf有一個(gè)基本判斷祷舀。 我都在猶豫是否要寫一篇這樣的文章來讓更多的人了解Thymeleaf真相,畢竟有“利益”沖突,怕難以服眾和被嘲笑裳扯。真心希望看了此文的開發(fā)人員支持我的化能點(diǎn)贊留言支持抛丽,不支持我的化請(qǐng)說出你的明確觀點(diǎn)。

最后饰豺,想對(duì)Thymeleaf說一句們我們行業(yè)內(nèi)的一句經(jīng)常調(diào)侃的話亿鲜,“老外的東西,只有到了中國好使冤吨,那才是真正的好使”蒿柳。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市漩蟆,隨后出現(xiàn)的幾起案子垒探,更是在濱河造成了極大的恐慌,老刑警劉巖怠李,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件圾叼,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡捺癞,警方通過查閱死者的電腦和手機(jī)夷蚊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來髓介,“玉大人惕鼓,你說我怎么就攤上這事“姹#” “怎么了呜笑?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長彻犁。 經(jīng)常有香客問我叫胁,道長,這世上最難降的妖魔是什么汞幢? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任驼鹅,我火速辦了婚禮,結(jié)果婚禮上森篷,老公的妹妹穿的比我還像新娘输钩。我一直安慰自己,他們只是感情好仲智,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布买乃。 她就那樣靜靜地躺著,像睡著了一般钓辆。 火紅的嫁衣襯著肌膚如雪剪验。 梳的紋絲不亂的頭發(fā)上肴焊,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音功戚,去河邊找鬼娶眷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛啸臀,可吹牛的內(nèi)容都是我干的届宠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼乘粒,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼豌注!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谓厘,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤幌羞,失蹤者是張志新(化名)和其女友劉穎寸谜,沒想到半個(gè)月后竟稳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熊痴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年他爸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片果善。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诊笤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出巾陕,到底是詐尸還是另有隱情讨跟,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布鄙煤,位于F島的核電站晾匠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏梯刚。R本人自食惡果不足惜凉馆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望亡资。 院中可真熱鬧澜共,春花似錦、人聲如沸锥腻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘦黑。三九已至京革,卻和暖如春销睁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背存崖。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國打工冻记, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人来惧。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓冗栗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親供搀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子隅居,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容