关于Thymeleaf的真相

原创
2017/08/09 21:59
阅读数 4.8W

Thymeleaf 一直以来都是个使用小众的模板引擎,在2.0以前,最为人吐槽的是性能跌到无底线。甚至朋友的项目因为Thymeleaf性能慢,影响到整个项目慢。Stackoveflow 社区也有很多人吐槽影响了自己的系统性能。 总所周知,系统单纯某一方面性能慢很难影响整个系统性能,如果系统慢,最有可能的是数据存取出问题,然而Thymeleaf能导致系统奇慢无比,确实是开源软件头一遭。 如果你不相信Thymeleaf2.0性能慢,可以参考 mbosecke/template-benchmark,或者国内的一个评测 https://my.oschina.net/smile622/blog/339884 (顺便提一下,这俩个性能基准测试,beetl都是最高的)

输入图片说明

据说Thymeleaf3.0性能成倍的提高了,在我用上面的基准测试汇总,Thymeleaf3.0仍然比最慢的Freemaker还慢很多。希望3.0不会影响到系统性能

输入图片说明

关于性能这一块,并不是我打算揭示的真相,毕竟这一块,早有定论。我要揭示的Thymeleaf真相,是它宣称的浏览器能直接打开,适合前端开发这个特点,还有他宣称所谓的优雅语法,以及谣言它是Spring Boot 默认模板引擎

浏览器直接能打开模板?

Thymeleaf 网站首页重点介绍的是使用Thymeleaf编写的模板能直接用浏览器打开,所以适合前端开发使用。这是因果关系。可我认为,这个因也错了,果也错了。

Thymeleaf编写的模板并不是所有都可以用浏览器打开,简单的模板页面可以。这点Beetl也能做到。复杂的模板页面,Thymeleaf编写的模板用浏览器打开照样没有效果。

Thymeleaf官网首页忽悠出来的第一个例子举例子吧

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

这个模板给人的感觉似乎确实能让浏览器打开。因为他使用了th:text 属性,浏览器会忽略这个属性。所以浏览器打开,看的像一个静态的html页面。

如果所有的模板页面都是这么简单,那么beetl也能做到这点,比如定义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改写的代码照样能用浏览器打开,你也许会认为,beetl模板打开后出现了占位符,貌似比Thymeleaf差一点,但我却认为,如果浏览器打开Thymeleaf模板的都是静态文本,你都不清楚哪儿是静态文本,哪儿是动态文本需要关注,这点还真不如Beetl更适合。

Beetl当然不是为了所谓能浏览器打开的模板设计的模板语言,只是尽力做到了浏览器能打开,比如Beetl的include标签实际上就努力尝试在重用模板的时候,又可以尽量让前端人员理解include内容,比如

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

这就是内置的include,为什么会带有{},从beetl设计刚开始的愿景就是能让前端团队尽量理解模板引擎。 然而,我知道,光靠浏览器能打开并不能做到这一点,况且,我认为Thymeleaf在这个功能宣传上过于夸大,并不能做到。

比如,还是上面这个例子,我需要根据条件判断以确定<td></td> 里显示的内容,那么我可能这么写

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

上述增加了一个if语句,比如admin情况下,可以显示名称。这样,一个tr下会出现三个td,这显然与th不对应,这样的Thymeleaf会显示成什么样呢?显然不是Thymeleaf所宣称的那个样子

再以Thymeleaf官网文档switch例子说明,如下一个switch语句用法,你真希望在浏览器显示3个段落吗?显然不是。

<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做了最大的努力,但遇到复杂的模板,我的意思是,在我们项目,哪怕最简单的一个项目,Thymeleaf模板都不可能实现他所谓的浏览器打开,这是个美好的愿望,Beetl 从7年前开始研发哪一天也有这样的愿望,但我深知是做不到的,Thymeleaf在努力做,花费了巨大的代价(庞大的奇怪的语法,会在后面提到),但不可能做到。

浏览器能预览模板又能怎么样?

Thymeleaf一直宣传的优势是能浏览器打开模板,前面已经说道,不可能实现。那么问题来了,就算简单的模板,浏览器打开了,对前端开发人员有多大意义(这里潜台词是前后分离方案)

我在Thymeleaf官网没有看到Thymeleaf对前后端分离的具体措施,也没有看到互联网上任何文档说明如何使用Thymeleaf做前后分离,反到是Beetl,提供了充分的前后端分离方案。

作为前端人员,如果他真的用Thymeleaf编写模板,他最大的需要并不是Thymeleaf反复提及的能在浏览器打开模板就行,这并不能保证模板是万无一失,可以放心交给后端人员集成了。他最需要的是能模拟后端各种数据,对模板做各种测试,好像模板真的经过后端渲染一样。这才是分离开发,这才前端人员真正需要的。 Beetl才能真正做到这一点,Beetl使用前端人员熟悉的JS语法和HTML扩展标签编写模板,Beetl的WebSimulate 插件能让前端人员去模拟后台数据,模拟出各种数据,各种分支情况,像真的后台调用一样。这时候,前端人员,打开浏览器访问,这才是真正的终极“浏览器打开模板”,而不是Thymeleaf那种假的“浏览器能预览模板”

Thymeleaf为这种假的“浏览器能预览模板” 付出了巨大的语法代价,如果你浏览Thymeleaf技术文档,你会发现,不可能让前端人员掌握Thymeleaf语法,你看看我的一个Thymeleaf部分语法贴图你就知道了 输入图片说明

这部分语法只占Thymeleaf整体语法的5-10%。Thymeleaf的推崇者,你们确定以及肯定前端人员会去使用吗?Beetl不仅仅能前段后端分离,语法比这少多了,而且,都是前端人员熟悉的类似JS的语法。孰优孰劣,一看就知道。

所谓Thymeleaf的优雅语法

国内的Thymeleaf推崇者认为它的语法优雅。恕我直言,我实在欣赏不来Thymeleaf语法,我会列举出官网文档的语法例子,各位看官凭直觉看一看

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

以上表达式共同特点都是有{}符号,但不同的是前面有不同的符号,表示不同的意思,我这里懒得解释,因为Thymeleaf我也是新手..... 不小心非常容易出错,Beetl则只有一个标准的${} 引用。

如果你认为{} 符号很明确的话,那你就大错特错了,

{}
{{}}

{{}} 又完全是另外一个意思,这不符合程序员直觉,因为所有语言里,{} 和 {{}} 效果是一样的,Thymeleaf却表达了不同的含义。

Thymeleaf显然喜欢组合不同的符号来完成特定的模板渲染,其他模板语言则通常使用更为常见的函数调用,格式化函数来完成。一个模板渲染引擎,真没有必要搞那么复杂语法。

如果这只是恶心到你,但并没有让你混乱,那我们接着看看Thymeleaf更多的语法

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

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

这俩种是不同写法,但是等价的,前提是前者使OGNL引擎,后者使用了SpringEL引擎。在Spring上下文里,Thymeleaf的表现有很大不同,所以你要非常清楚,不同的引擎,Thymeleaf应该怎么去写。

你也许有点混乱了,不知道下面的写法是不是更为混乱(都是官网的例子)

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

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

第一段如果你没看懂它的意思,我也不会解释。 至于第二段代码,意义较为明确,但如果我想扩展这个表达式,增加一个变量,那我应该是下面那种写法呢?

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

或者

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

我并不清楚,Thymeleaf怎么写,更体会不到Thymeleaf的优雅了。 如果是beetl模板,就很明确,就是一个类似js的表达式,在占位符里${ } 随意书写

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

如你所见,Thymeleaf语法体系非常混乱,而且语法庞大,我上面的那个贴图不过Thymeleaf的语法5%左右,还有更多的Thymeleaf等待那些所谓的推崇者去挖掘,去写博客介绍,去介绍“回字的四种写法”(来自鲁迅的《孔乙己》)

Beetl很少有使用者写博客介绍Beetl,这是因为Beetl足够简单,普通功能真没有写博客介绍的必要。

Spring Boot 所谓内置Thymeleaf

国内有些Thymeleaf推崇者试图以Spring Boot 所谓内置Thymeleaf来表明使用Thymeleaf是一条很正确的道路。实际上并不是这样

首先,Spring Boot 每一类型技术,都会集成几种技术,比如Cache, 集成了自家的Redis,也有EhCache,Hazelcast,Spring Boot 并没有明确说出来,Spring Boot 推荐使用哪种技术。Spring Boot提供了多种选择。这非常公平

其次,Spring Boot 默认配置并不是代表最好。以REST Template为例子,Spring Boot默认使用的是JDK URL Connection,这难道比其他可选的HTTPClient,OKHttp更好吗?显然不是,还有Spring Boot提供的数据库连接池,是Tomcat的一款,这难道会比Druid,HikariCP更好,显然也不是

最后,Spring Boot为什么会在模板引擎里集成除了Freemaker,Groovy外,还会集成Thymeleaf呢,我想最大的原因是Thymeleaf深度使用了Spring技术,比如上一节提到的Spring SpEL(相当于其核心使用了SpEL),还有未提到,Thymeleaf官网说的Conversion Service 。正是因为这种如此深度集成,才使得Spring Boot 会选择Thymeleaf作为其中的一个模板引擎候选。而Velocity作为Apache体系技术,且7年不维护(恰好今天2.0发布了),当然不会作为候选。

Beetl是我7年前开始研发的模板语言,广泛在国内使用,客户有一流的互联网公司,国内大型企业,也有小型的创业公司,个人用户。Beetl是真正经得起考验的技术。很长时间来,我在模板引擎领域积累的经验足以使我对Thymeleaf有一个基本判断。 我都在犹豫是否要写一篇这样的文章来让更多的人了解Thymeleaf真相,毕竟有“利益”冲突,怕难以服众和被嘲笑。真心希望看了此文的开发人员支持我的化能点赞留言支持,不支持我的化请说出你的明确观点。

最后,想对Thymeleaf说一句们我们行业内的一句经常调侃的话,“老外的东西,只有到了中国好使,那才是真正的好使”。

展开阅读全文
加载中
点击加入讨论🔥(116) 发布并加入讨论🔥
116 评论
28 收藏
23
分享
返回顶部
顶部