Skip to content

10 属性优先级

当你在同一个标签中写多个 th:* 属性时,会发生什么, 比如这个遍历+文本渲染的例子:

html
<li th:each="item : ${items}" th:text="${item.description}">Item description here...</li>

我们需要 th:each 先执行(遍历生成每个 <li>),再执行 th:text(渲染当前 item 的描述),这才是我们想要的结果。HTML/XML 标准本身不对属性顺序赋予任何语义,也不会按属性在标签中的书写顺序执行,所以必须通过优先级机制保证逻辑执行的正确性。

所以,Thymeleaf 遵循内置的属性优先级规则

优先级顺序功能分类对应属性
1片段引入th:insertth:replace
2片段遍历th:each
3条件判断th:ifth:unlessth:switchth:case
4局部变量定义th:objectth:with
5通用属性修改th:attrth:attrprependth:attrappend
6特定属性修改th:valueth:hrefth:src(所有针对单个 HTML 属性的 th:* 都属于此类)
7文本/标签体修改th:textth:utext
8片段声明th:fragment
9片段移除th:remove

这就是属性优先级的作用:无论书写顺序如何,优先级高的属性先执行。(顺序调换,会导致可读性不高)

html
<!-- 顺序调换,但执行逻辑不变 -->
<li th:text="${item.description}" th:each="item : ${items}">Item description here...</li>

优先级规则的实际应用示例

示例1:遍历 + 条件判断 + 文本渲染

html
<!-- 执行顺序:th:each(2)→ th:if(3)→ th:text(7) -->
<li th:each="item : ${items}" 
    th:if="${item.active}" 
    th:text="${item.name}">Item name</li>

逻辑解析:

  1. 先执行 th:each 遍历 items,生成每个 item 对应的 <li>
  2. 再执行 th:if 判断当前 item 是否激活,未激活则跳过;
  3. 最后执行 th:text 渲染激活 item 的名称。

示例2:局部变量 + 链接生成

html
<!-- 执行顺序:th:with(4)→ th:href(6) -->
<a th:with="prodId=${product.id}" 
   th:href="@{/product/detail(id=${prodId})}">View Detail</a>

逻辑解析:

  1. 先执行 th:with 定义局部变量 prodId
  2. 再执行 th:href 利用 prodId 生成链接(若反过来写,结果也不变)。

示例3:片段引入 + 条件移除

html
<!-- 执行顺序:th:insert(1)→ th:remove(9) -->
<div th:insert="common :: adminBar" 
     th:remove="${!user.isAdmin}? all : none">Admin Bar</div>

逻辑解析:

  1. 先执行 th:insert 引入管理员栏片段;
  2. 再执行 th:remove 判断是否移除(非管理员则移除整个 div)。

关键注意事项

  • 优先级高于书写顺序:无论属性在标签中写在前面还是后面,都严格按优先级执行(比如 th:text 永远在 th:each 之后执行);
  • 可读性建议:虽然顺序不影响执行,但建议按“高优先级→低优先级”的顺序书写属性(如先 th:eachth:text),提升代码可读性;
  • 同优先级属性:同一优先级的属性(如 th:ifth:unless)会按书写顺序执行,但通常不建议在同一标签中写同优先级的互斥属性(如同时写 th:ifth:unless)。

总结

  1. Thymeleaf 通过属性优先级解决多属性的执行顺序问题,与书写顺序无关;
  2. 核心优先级逻辑:片段引入 > 遍历 > 条件判断 > 变量定义 > 属性修改 > 文本渲染 > 片段声明 > 移除;
  3. 书写时建议按优先级顺序排列属性,兼顾执行正确性和代码可读性。