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)題")](https://static.oschina.net/uploads/img/201708/09224916_VhrM.png)
這部分語法只占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)侃的話亿鲜,“老外的東西,只有到了中國好使冤吨,那才是真正的好使”蒿柳。