Thymeleaf 是一个服务器端 Java 模板引擎,能够处理 HTML、XML、CSS、JAVASCRIPT 等模板文件。Thymeleaf 模板可以直接当作静态原型来使用,它主要目标是为开发者的开发工作流程带来优雅的自然模板,也是 Java 服务器端 HTML5 开发的理想选择。
创建模板文件
创建一个html模板文件
1 | <!DOCTYPE HTML> |
要点:
- 引入Thymeleaf命名空间
<html xmlns:th="http://www.thymeleaf.org">
; - HTML5中可以data-th-*替换th:*从而不用引入命名空间;
<p data-th-text="${message}">Welcome to BeiJing!</p>
标准表达式语法
Thymeleaf提供了非常丰富的标准表达式语法:
- 简单表达式
- 字面值
- 文本操作
- 算术运算
- 布尔运算
- 比较和相等
- 条件运算
- 无操作符
简单表达式
语法 | 名称 | 描述 | 作用 |
---|---|---|---|
${…} | Variable Expressions | 变量表达式 | 取上线文变量值 |
*{…} | Selection Variable Expressions | 选择表达式 | 取选择的对象的属性值 |
#{…} | Message Expressions | 消息表达式 | 使文字国际化,l18N |
@{…} | Link URL Expressions | 连接表达式 | 标识超链接地址 |
~{…} | Fragment Expressions | 片段表达式 | 引用一段公共的代码片段 |
${…}
1 | @GetMapping("/standard-expression-syntax/variables") |
通过变量表达式${}
取出上线文环境中的message变量:
1 | <!-- Welcome to BeiJing! --> |
相当于
1 | ctx.getVariable("message"); |
*{…}
与th:object配合使用,示例:
1 | <div th:object="${session.user}"> |
相当于
1 | <div> |
如果对象没有被选择,那么,*{}和${}表达式所达到的效果是完全相同的:
1 | <p th:text="*{session.user.name}"></p> |
#{…}
消息表达式可用于国际化文字信息。i18n资源文件命名规则:
- basename.properties
- basename_language.properties
- basename_language_country.properties
basename是自定义的资源文件名称,language和country必须是Java支持的预言和国家。basename.properties是缺省加载的资源文件。
创建src/main/resources/messages.properties
1 | welcome.message=北京欢迎你! |
创建src/main/resources/messages_en_US.properties
1 | welcome.message = Welcome to BeiJing! |
messages是SpringBoot 加载资源文件默认采用的名称(basename),如果你所使用的资源文件名称不是以message命名或者所使用的资源文件不再src/main/resources
根目录,你可以通过spring.messages.basename属性来做具体的配置。假设他们在目录src/main/resources/i18n
。
application.properties配置示例:
1 | spring.messages.basename:i18n/messages |
application.yml配置示例:
1 | spring |
静态文本消息示例:
1 | <!-- 北京欢迎你! --> |
消息表达式#{}是不允许直接处理非静态文本消息的,但是你可以在资源文件中通过使用占位符{}来处理非静态文本消息:
message.properties配置示例:
1 | welcome.user.message={0},北京欢迎你! |
message_en_US.properties配置示例:
1 | welcome.user.message={0},Welcome to BeiJing! |
非静态文本消息,以参数的形式传递变量值:
1 | <!-- fanlychie, 北京欢迎你! --> |
@{…}
连接表达式@{}是专门处理URL连接地址的。
绝对地址示例:
1 | <!-- https://fanlychie.github.io --> |
页面相对地址示例:
1 | <!-- commons/base.html --> |
上下文相对地址(相对于当前的服务)示例:
1 | <!-- /css/mian.css --> |
服务器相对地址(相对于部署在同一个服务器中的不同服务)示例:
1 | <!-- /image/upload --> |
参数使用示例:
1 | <!-- /css/mian.css?v=1.0 --> |
~{…}
片段表达式~{}可以用来引用一段公共的 HTML 代码片段。
语法 | 描述 |
---|---|
~{templatename} | 引用整个模板文件的代码片段 |
~{templatename :: selector} | selector 可以是 th:fragment 指定的名称或其他选择器。如类选择器、ID选择器等 |
~{::selector} | 相当于 ~{this :: selector},表示引用当前模板定义的代码片段 |
在 Thymeleaf 模板文件中,你可以使用th:fragment属性来定义一段公共的代码片段,然后你可以通过使用th:insert、th:replace、th:include(Thymeleaf 3.0 开始不再推荐使用,本文也将不再介绍它)属性来将这些公共的代码片段引入到模板文件中来。
src/main/resources/templates/base.html,通过th:fragment属性定义一段公共的代码片段:
1 | <div id="footer" th:fragment="footerFragment">© 2017 fanlychie</div> |
src/main/resources/templates/index.html,通过th:insert属性引用一段公共的代码片段:
1 | <div th:insert="~{base :: footerFragment}"></div> |
其中,~{}是可选的,我们可以去掉这层的包裹:
1 | <div th:insert="base :: footerFragment"></div> |
若 index.html 与 base.html 不在同级目录,如 templates/commons/base.html:
1 | <div th:insert="~{commons/base :: footerFragment}"></div> |
使用th:fragment属性定义代码片段时,你还可以声明一组参数:
1 | <div th:fragment="crumbs(parent, child)"> |
此外,我们还可以通过类选择器、ID选择器等来引用公共的代码片段:
1 | <div th:insert="~{base :: #footer}"></div> |
除了th:insert属性th:replace的区别:
1 | <!-- |
内置对象
对象 | 描述 |
---|---|
#ctx | 上下文对象 |
#vars | 同#ctx,标识上下文变量 |
#locale | 上下文(特定的地理区域)变量,参考:java.util.Locale |
#request | HttpServletRequest对象 |
#response | HttpServletResponse |
#session | HttpSession对象 |
#servletContext | ServletCOntext对象 |
#ctx示例:
1 | <!-- zh_CN --> |
#vars示例:
1 | <!-- zh_CN --> |
#locale示例:
1 | <!-- zh_CN --> |
#request示例:
1 | <!-- HTTP/1.1 --> |
注意,请求地址的 URL 参数直接通过#request.x是取不出来的,需要使用param.x语法来取出。如,URL:/standard-expression-syntax/variables?q=expression,取出 q 参数的正确姿势:
1 | <p th:text="${param.q}"></p> |
#response示例:
1 | <!-- 200 --> |
#session示例:
1 | <!-- 2BCB2A0EACFF2D9D249D9799431B5127 --> |
注意,放到会话里面的对象直接通过#session.x是取不出来的,需要使用session.x语法来取出。如,取出会话里面的 user 对象的正确姿势:
1 | <p th:text="${session.user.name}"></p> |
工具类
对象 | 描述 |
---|---|
#messages | 消息工具类,与#{}作用相同 |
#uris | 地址工具类 |
#conversions | 对象转换工具类 |
#dates | 日期对象工具类 |
#calendars | 日历工具类 |
#numbsers | 数字工具类 |
#strings | 字符串工具类 |
#objects | 对象工具类 |
#bools | 布尔工具类 |
#arrays | 数组工具类 |
#lists | List工具类 |
#sets | Set工具类 |
#maps | Map工具类 |
1 | <!-- false --> |
字面值
所谓字面值,首先它不是一个变量,它是一个具体的确切的值,通常这些值是比较简单的,例如:18,welcome 等,没有名称,以至于我们只能用值来称呼他们,因此我们称其为字面值。
文字字面值
文字字面值是用单引号引起来的任何字符内容,如果字符内容里面含有单引号,则需要进行转义:
1 | <!-- Welcome to BeiJing! --> |
数字字面值
1 | <!-- 2017 --> |
布尔字面值
1 | <!-- false --> |
空字面值
1 | <!-- false --> |
字面令牌
字面令牌(Literal Tokens)的内容只能含有(不能含有空格、特殊符号等):
- 大写或小写的字母、中文等不含空格和特殊符号的文本
- 0 到 9 的数字
- 中括号
- 下划线
- 连字符(-)
- 点符号(.)
实际上,数字、布尔和空字面值都是字面令牌的特殊情况。字面令牌能够用来对标准表达式语法进行简化,我们可以将包裹它的内容的单引号去掉:
1 | <p th:text="Welcome to BeiJing!"></p> |
它等效于:
1 | <p th:text="'Welcome to BeiJing!'"></p> |
文本操作
字符串连接
不管是字面值还是表达式的结果,我们都可以使用+符号将它们连接起来:
1 | <!-- Welcome to BeiJing! --> |
字面值替换
符号||可以用来将字面值和表达式包裹起来,这样就能方便的替换变量的值,而不需要使用+连接符:
1 | <!-- Welcome to BeiJing! --> |
算术运算
支持+(加)、-(减)、*(乘)、/(除)、%(模)运算:
1 | <!-- 6 --> |
布尔运算
支持and(且)、or(或)、!(非)、not(非)运算:
1 | <p th:text="${user.online and user.vip}"></p> |
比较和相等
支持<(lt)、>(gt)、<=(le)、>=(ge)、==(eq)、!=(ne):
1 | <p th:text="${user.age < 60}"></p> |
条件运算
三元运算符:(if) ? (then) : (else)
1 | <p th:text="${user.online ? '在线' : '离线'}"></p> |
二元运算符:(value) ?: (defaultValue)。
其中,value非空(null)即真,条件为真时输出value,否则输出defaultValue。假设token = null,user.email = fanlychie@gmail.com
1 | <!-- 你还没有登录,请先登录 --> |
无操作符
当模板运行在服务器端时,Thymeleaf 会解析th:*属性的具体值替换标签体的内容。无操作符(_)则允许你使用原型标签体的内容作为默认值:
1 | <!-- 你还没有登录,请先登录 --> |
使用文本
首先介绍两个最基础的th:*属th:text和th:utext,它们都是用于处理文本消息内容。
th:text
在标签体中展示表达式评估结果的文本内容:
1 | <p th:text="${message}"></p> |
使用外部化的文本内容:
1 | <p th:text="${message}">Welcome to BeiJing!</p> |
th:utext
属性th:utext与th:text的区别在于:
- th:text默认会对含有 HTML 标签的内容进行字符转义;
- th:utext(Unescaped Text)则不会对含有 HTML 标签的内容进行字符转义;
假设:
1 | message = "<b>Welcome to BeiJing!</b>"。 |
使用th:text属性:
1 | <!-- <b>Welcome to BeiJing!</b> --> |
使用th:utext属性:
1 | <-- Welcome to BeiJing! --> |
设置属性值
在 Thymeleaf 模板文件中,你可以使用th:*
(或者使用th:attr属性)来设置任意的 HTML5 标签属性的值。不仅如此,你还可以th:*-*
来同时为多个不同的标签属性设置相同的一个值,甚至你可以使用th:attrappend和th:attrprepend来追加新的值到现有的标签属性值中。
th:attr
这种方式是不被推荐的,了解一下就行。下面是用th:attr="href=..."
来设置标签href属性的值:
1 | <a th:attr="href=@{https://www.google.com.hk}">谷歌一下你就知道</a> |
th:*
显然th:attr="href=@{http://www.baidu.com}"
不够简洁,我们更推荐下面的这种语法:
1 | <a th:href="@{https://www.google.com.hk}">谷歌一下你就知道</a> |
th:*-*
如果想要同时为标签的多个不同属性设置相同的一个值,可以使用
th:*-* 的语法:
1 | <img src="logo.png" th:alt-title="LOGO图片"> |
相当于
1 | <img src="logo.png" th:alt="LOGO图片" th:title="LOGO图片"> |
th:attrappend&th:attrprepend
th:attrappend和th:attrprepend可以将表达式的结果分别追加到指定的属性值之后和之前。
1 | <!-- <button class="btn enable">购买</button> --> |
th:classappend&th:styleappend
它们分别用来代替th:attrappend="class=…"和th:attrappend=“style=…”。
1 | <!-- <button class="btn enable">购买</button> --> |
布尔属性
在 HTML 中有些属性是布尔属性,布尔属性是指没有值的属性,如readonly、checked、selected等。它们若存在那就意味着值为 true。
1 | <input type="checkbox" name="rememberme" checked /> 记住我 |
Thymeleaf 也允许我们通过th:*(这里的*表示任意的布尔属性) 来选择是否使用这些布尔属性。
1 | <input type="checkbox" name="rememberme" ch:checked="${rememberme}" /> 记住我 |
正如你所见,如果表达式的结果为true,则自动勾选复选框,若为false,则不会自动勾选。
遍历
遍历(迭代)的语法th:each=“自定义的元素变量名称 : ${集合变量名称}”:
1 | <div> |
属性 | 类型 | 描述 |
---|---|---|
index | int | 当前迭代索引,0开始 |
count | int | 当前迭代技术,1开始 |
size | int | 集合中元素总个数 |
current | int | 当前元素对象 |
even | boolean | 当前迭代的计数是否是偶数 |
odd | boolean | 当前迭代的计数是否是奇数 |
first | boolean | 当前元素是否是集合的第一个元素 |
last | boolean | 当前元素是否是集合的最后一个元素 |
状态变量的使用语法:th:each=“自定义的元素变量名称, 自定义的状态变量名称 : ${集合变量名称}”:
1 | <div> |
不管什么时候,Thymeleaf 始终会为每个th:each创建一个状态变量,默认的状态变量名称就是自定义的元素变量名称后面加Stat字符串组成:
1 | <div> |
条件判断
条件判断语句有三种,分别是:th:if、th:unless、th:swith。
th:if
表达式的评估结果为真时则显示内容,否则不显示:
1 | <a th:href="@{/user/order(uid=${user.id})}" th:if="${user != null}">我的订单</a> |
真假评估的依据:
- 当表达式的值不为空(null)时
- 如果表达式的值是一个布尔类型,且值为true评估为真,否则为假
- 如果表达式的值是一个数字类型,且值为非0评估为真,否则为假
- 如果表达式的值是一个字符类型,且值为非0评估为真,否则为假
- 如果表达式的值是一个字符串类型,且值为非"false"、“off”、"no"评估为真,否则为假
- 如果表达式的值不是一个布尔、数字、字符或字符串评估为真
- 当表达式的值为空(null)时,评估结果为假
1 | <a th:href="@{/user/order(uid=${user.id})}" th:if="${user}">我的订单</a> |
但是,为了代码的可读性,我们并不建议这样使用。
th:unless
th:unless与th:if判断恰好相反,当表达式的评估结果为假时则显示内容,否则不显示:
1 | <a th:href="@{/user/order(uid=${user.id})}" th:unless="${user == null}">我的订单</a> |
th:swith
多路选择语句,它需要搭配th:case来使用:
1 | <div th:switch="${user.role}"> |
定义局部变量
使用th:with属性可以定义局部变量:
1 | <p th:with="name='fanlychie'"> |
同时定义多个局部变量时,用英文,号分隔开:
1 | <p th:with="name=${user.name},age={user.age}"> |
注释
下面介绍常见的两种注释:
标准注释
单行注释
1 | <!-- <span>${message}</span> ---> |
多行注释
1 | <!-- |
解析器级注释
语法:<!--/* ... */-->
,注释的代码块会在引擎解析的时候抹去。
单行注释
1 | <!--/* <span>${message}</span> */--> |
多行注释
1 | <!--/*--> |
内联表达式
内联表达式允许我们直接在 HTML 文本中使用标准表达式,而不需要使用th:*标签属性。
[[...]]
1 | ``` |
[()]
1 | ``` |
th:inline
我们已经了解到,使用[[]]
和[()]
语法可以直接在 HTML 文本中使用标准表达式,如果想要使用更多高级的功能,需要使用th:inline属性来激活,它的取值如下:
值 | 描述 |
---|---|
none | 进制内联表达式,可以原样输出[[]][()]字符串 |
text | 文本内联,可以使用th:each等高级语法 |
css | 样式内联,如:style th:inline=“css” |
javascript | 脚本内联 th:inline=“javascript” |
none
1 | <!-- [[1, 2], [3, 4]] --> |
text
1 | <!-- 北京 上海 广州 深圳 --> |
css
1 | <style th:inline="css"> |
javascript
1 | <script th:inline="javascript"> |
参考文档:
https://fanlychie.github.io/post/thymeleaf.html
http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html