Thymeleaf是一个和Velocity、FreeMarker 类似的模板引擎,它在有网络和无网络的环境下皆可运行。因为它支持html原型,在html的标签里增加了额外的属性来达到模板+数据的展示方式。浏览器解释html时会忽略未定义的标签属性,所以thymeleaf的模板可以静态地运行。当有数据返回到页面时,Thymeleaf标签会动态地替换掉静态内容,使页面动态显示。
它与SpringBoot完美结合,SpringBoot提供了Thymeleaf的默认配置,并且为Thymeleaf设置了视图解析器。它可以快速实现表单绑定、属性编辑器、国际化等功能。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
SpringBoot自动为Thymealf注册了一个视图解析器ThymealfViewResolver,并且配置了模板(HTML)的位置,与JSP类似的前缀+视图名+后缀的风格。我们可以进到它的配置文件(ThymeleafProperties)里去看:
可以发现,如果我们没有在yml
中进行配置,则它去找的视图文件默认是在resources
配置文件夹下的templates
文件夹里,后缀为.html
。我们也可以在配置文件中进行配置,即图中所标注的spring.thymeleaf
下。
yml配置文件
基本
使用<html lang="en" xmlns:th="http://www.thymeleaf.org">
在html页面中,要使用thymealf的标签,只需在原标签名前加th:
即可,如th:src
、th:href
、th:text
等
①${}
,变量表达式,它可以获取到Controller层存入Model的值
// 后端Controller层代码
model.addAttribute("name","柳成荫");
User user = new User("柳成荫",22); // 姓名、年龄
model.addAttribute("user",user);
// html页面中获取并显示
<span th:text="${name}">九月span>
<span th:text="${user.age}">18span>
前面说过,当有数据传递过来时,则动态数据会替换静态数据。
②*{}
,选择变量表达式,可以省略对象名,直接获取属性值
<div th:object="${user}">
<p th:text="*{name}">九月p>
<p th:text="*P{age}">18p>
div>
③#{}
,Message表达式,它主要是从国际化配置文件中取值,这里暂不做示例,文章后面将会示例从国际化配置文件中取值。
①绝对网址,绝对URL用于创建到其他服务器的链接,需要指定协议名称http
或者https
,如:
<a th:href="@{https://www.baidu.com}">百度a>
②上下文相关URL,即与项目根相关联的URL,这里假设我们的war包为app.war,且没有在tomcat的server.xml
配置项目的路径(Context),则在Tomcat启动之后,就会在webapps文件下产生一个app文件夹,此时app
就是上下文路径(Context)
<a th:href="@{/blog/search}">跳转a>
<a href="/app/blog/search">跳转a>
③服务器相关URL,它与上下文路径很相似,它主要用于一个Tomcat下运行有多个项目的情况。比如说我们当前项目为app
,如果tomcat还运行着一个otherApp
,我们就可以通过该方法访问otherApp
的请求路径。
<a th:href="@{~/otherApp/blog/search}">跳转a>
<a href="/otherApp/blog/search">跳转a>
④有时候我们需要页面带参数传递到后端,则可以使用下面这个方法
<a th:href="@{/blog/search(id=3,blogName='Java')}" >跳转a>
<a href="/blog/search?id=3&blogName=Java" >跳转a>
<a th:href="/blog/{id}/search(id=3&blogName=Java)">跳转a>
<a href="/blog/3/search?blogName=java">跳转a>
有时候,我们需要在指令中填入基本类型如:字符串、数值、布尔等,不希望Thymeleaf去给我们解析,可以这样做:
①字符串字面值
<span th:text="‘Thymealf’ + 3">templatesspan>
<span>Thymealf3span>
②数字字面值
<span th:text="1 + 3">templatesspan>
<span>4span>
③布尔字面值:为true和false
传统拼接需要用''
来进行普通字符串和表达式的拼接,Thymeleaf中进行了简化,只需将拼接的内容块使用||
包裹即可:
<span th:text="|欢迎您,${user.name}|">九月span>
①因为html里会将<
和>
符号进行解析,所以不能直接使用,但是如果在{}
内使用,是不需要转换的
> gt 即greater than,大于
< lt 即less than,小于
>= ge 即greater equal,大于等于
<= le 即less equal,小于等于
②三元运算符
<span th:text="${false} ? '男' : '女'">性别span>
<>
③空值判断
<span th:text="${user.name} ?: ‘空值显示’">span>
①[()]
,解析输出,会解析内容里的html
标签
<span>[(${user.name})]span>
②[[]]
,原样输出,不会解析内容里的html
标签
<span>[[${user.name}]]span>
可以将后端传来的数据赋值给一个局部变量
,这个局部变量
只能在标签内部
使用,外部是不可以的
<div th:with="user=${userList[0]}">
<span th:text="${user.name}">昵称span>
div>
①th:if
,满足条件才显示标签包裹的(含标签)的内容
<span th:if="${true}">显示span>
<span th:if="${user.age == 18}">18岁显示span>
②th:unless
,与th:if
相反,不满足条件时显示
<span th:unless='${true}'>不显示span>
③th:switch
,switch的效果一致
<div th:witch="${user.name}">
<span th:case="柳成荫">柳成荫span>
<span th:case="九月">九月span>
<span th:case="寻宝">寻宝span>
div>
th:each
,迭代一个集合/数组,可以使用它的内置对象stat
获取更多的信息。
先列出内置对象stat
的用法:
index:角标,从0开始
count:元素的个数,从1开始,当前遍历到第几个
size:元素的总个数
current:当前遍历到的元素
even/odd:是否为奇/偶,都是返回true或false的布尔结果
first/last:是否第一/最后,都是返回true或false的布尔结果
使用th:each
<tr th:each="user:${userList}">
<td th:text="${user.name}">td>
<td th:text="|当前迭代到第${stat.count}个了|">td>
tr>
1、${#ctx}
:上下文对象,可用于获取其他内置对象
2、${#vars}
:上下文变量。
3、${#locale}
:上下文区域设置。
4、${#request}
:HttpServletRequest对象。
5、${#response}
:HttpServletResponse对象。
6、${#session}
:HttpSession对象。
7、${#servletContext}
:ServletContext对象。
具体怎么使用,可以自行百度,这里以session
为例:
<span th:text="${session.user.name}">存储在session的User对象span>
1、#strings
:字符串工具类
2、#lists
:List 工具类
3、#arrays
:数组工具类
4、#sets
:Set 工具类
5、#maps
:常用Map方法。
6、#objects
:一般对象类,通常用来判断非空
7、#bools
:常用的布尔方法。
8、#execInfo
:获取页面模板的处理信息。
9、#messages
:在变量表达式中获取外部消息的方法,与使用#{…}语法获取的方法相同。
10、#uris
:转义部分URL / URI的方法。
11、#conversions
:用于执行已配置的转换服务的方法。
12、#dates
:时间操作和时间格式化等。
13、#calendars
:用于更复杂时间的格式化。
14、#numbers
:格式化数字对象的方法。
15、#aggregates
:在数组或集合上创建聚合的方法。
16、#ids
:处理可能重复的id属性的方法。
具体怎么使用,可以自行百度,这里仅以#dates
为例,格式化时间:
<span th:text="${#dates.format(blog.updateTime)},'yyyy-MM-dd HH:mm:ss'">span>
使用th:classappend
,可以增加class
元素类名。通常用在如导航栏上,当导航栏上某个标签被选中,它会与其他没有被选中的有不同的样式。
<a class="item m-mobile-hide’ th:classappend='${n==1} ? ‘active’">首页a>
<a class="item m-mobile-hide active’">首页a>
布局
使用定义片段
和引入片段
是非常好的功能,在一个项目中的各个页面里,通常他们大部分导航栏和底部是相同的。我们可以创建一个页面专门用来定义重复使用片段,然后在其他页面做引入。
①使用th:fragment
定义通用片段 - 普通片段
<nav th:fragment="header">
<a href="#">首页a>
<a href="#">分类a>
nav>
②使用th:fragment
定义通用片段 - 带参片段
如下代码,如果我们当前是在首页,则首页这个标签就会多一个样式,而分类就没有。我们只需要在引入这块片段的代码里传递一个参数过来进行判断即可。
<nav th:fragment="header(n)">
<a class="item’ th:classappend='${n==1} ? ‘active’">首页a>
<a class="item’ th:classappend='${n==2} ? ‘active’">分类a>
nav>
③使用id
来定义片段 - 不推荐
<nav id="header">
<a href="#">首页a>
<a href="#">分类a>
nav>
_fragment
为定义片段的html页面①将公共的标签插入
到指定标签中 - 该方法不可以省略~{}
<div th:insert="~{_fragment::header}">
div>
②将公共的标签替换
指定的标签 - 该方法可以省略~{}
推荐
使用该引入方式,这样做,可以保证在没有网络(动态数据)的情况下,其他页面也有内容可以显示。
<div th:replace="~{_fragment::header}">
<a href="#">链接a>
div>
③将公共的标签包含
到指定的标签 - 该方法可以省略~{}
<div th:include="~{_fragment::header}">
div>
④上诉三种方法,可以用来引入使用id
定义片段的 - 不推荐
<nav th:insert="~{_fragment::#header}">
nav>
⑤引入带参片段 - 重要
<nav th:replace="header(2)">
<a class="item’ th:classappend='${n==1} ? ‘active’">首页a>
<a class="item’ th:classappend='${n==2} ? ‘active’">分类a>
nav>
th:fragment
更新数据很多时候,在页面里我们需要使用Ajax
异步请求数据,我们希望只更新某一块的数据,其他地方不变。这时,就需要th:fragment
来做了,文章前面有提到return "index :: blogList"
这种方式,它就是配合th:fragment
来做的。
<div th:fragment="userInfo">
<span th:text="${user.name}">九月span>
<span th:text="${user.age}">18span>
div>
public String userInfo(Model model){
model.addAttribute(user,new User("柳成荫",22));
return "index :: userInfo";
}
th:fragment
和th:replace
是用在合适的地方非常好用,常见的css块、js块、导航栏、底部区域。
th:block
th:block
是thymeleaf提供的块级标签,其特殊性在于Thymeleaf模板引擎在处理
的时候会删掉它本身,标签本身不显示,而保留其内容。
①常见用法 - HTML部分
<th:block th:if="...">
<div id="div1">我和div2一起div>
<div id="div2">我和div1一起div>
th:block>
<table>
<th:block th:each="...">
<tr>...tr>
<tr>...tr>
th:block>
table>
②常见用法 - JS部分 可能有人会问,这个 可以看到,上面我们使用了 ② 模板引擎不仅可以渲染html,也可以对JS中的进行 在SpringBoot项目里面做国际化,只需要在 2、要确保文件编码是 6、一般来说我们在页面会做个切换中英文的按钮来进行切换 7、做完上诉步骤,我们还需要编写一个本地解析器来进行解析 8、我们还需要写一个配置文件来表明 国际化就这样完成了,通过上面定义的切换语言按钮就可以切换了。
有时候js
的引入可能也是其他页面公有的,我们想定义一个js片段
,然后在其他页面引入。可以使用js片段
包裹起来,然后将这个th:block
了
<th:block th:fragment="script">
<script src="../static/js/jquery-3.3.1.min.js" th:src="@{/js/jquery-3.3.1.min.js}">script>
<script src="../static/js/semantic.min.js" th:src="@{/js/semantic.min.js}">script>
th:block>
<th:block th:replace="_fragment::script">
<script src="../static/js/jquery-3.3.1.min.js">script>
th:block>
th:block
会不会影响静态页面?实际上,它是不会影响静态页面的。如果你实在是担心,我们可以用可以使用下面这种方法
<script src="../static/js/jquery-3.3.1.min.js">script>
注释的使用
的方式将
th:block
注释掉了,但是你也发现,我们在里面使用了/*/ /*/
来包裹了标签。它是thymeleaf注释的一种,它在运行时,就不会把上面那些注释了的代码看做是注释,而是当做正常的处理。
/*
是另外一种,它的作用就是thymeleaf运行时,把包裹起来的内容注释掉。
①/*/
注释
/*
注释
<div>
<span th:each="blog:${blogList}" th:text="${blog.name}">博客1span>
<span th:each="blog:${blogList}">博客2span>
<span th:each="blog:${blogList}">博客3span>
<span th:each="blog:${blogList}">博客4span>
div>
JS模板预处理
预处理
。而且为了在纯静态环境下可以运行。在script
标签中通过th:inline="javascript"
来声明这是要特殊处理的js脚本。<script th:inline="javascript">
var username = /*[[${user.name}]]*/ ‘默认’;
var age = /*[[${user.age}]]*/ ‘18’;
console.log(username);
console.log(age);
script>
国际化
resources/i18n
路径下创建xxx.properties
、xxx_en_US.properties
、xxx_zh_CN.properties
即可。具体怎么做,将在下文进行演示。
在SpringBoot中有一个messageSourceAutoConfiguration
,它会自动管理国际化资源文件。
也就是说,我们在resources/i18n
创建的国际化配置文件前缀
名为message
时,SpringBoot会自动加载。
当然,我们也可以自己定义国际化文件名称,这里我们将国际化配置文件命名为login
。
1、去yml
配置文件中配置国际化spring
messages: i18n.login
UTF-8
,可以到idea的设置里去设置并让其自动转换,Editor
->File Encodings
3、创建i18n
文件夹及3个国际化配置文件,如图
其中login.properties
是基础配置文件(默认),如果你的浏览器它是其他语言比如法语,是没有办法国际化的,所以它就会采用login.properties
在idea里只要创建两个国际化的配置文件就会自动加入到Resource Bundle 'xxx'
,当然我们还是需要创建三个properties
配置文件。后缀必须按格式写,_en_US
就是英文,_zh_CN
就是中文。
4、编写国际化
我们直接点进login.properties,可以看到下方有个切换到Resource Bundle
的按钮,点击切换,添加国际化,然后分别在右侧写上对应国际化语言
想添加多少,就可以添加多少。
5、可以在Thymeleaf
页面进行获取了,用到前面所说的#{}
<button type="submit" th:text="#{login.btn}">button>
<a th:href="@{/login(lan='zn_CN')}">中文a>
<a th:href="@{/login(lan='en_US')}">英文a>
public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
// 接收语言的参数 - 传进来的就是形如'zh_CN'这样的参数
String lan = request.getParameter("lan");
// 使用默认的语言 - 在文中就是login.properties文件里配置的
Locale locale = Locale.getDefault();
// 判断接收的参数是否为空,不为空就设置为该语言
if(!StringUtils.isEmpty(lan)){
// 将参数分隔 - 假设传进来的是'zh_CN'
String[] s = lan.split("_");
// 语言编码:zh 地区编码:CN
locale = new Locale(s[0],s[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
国际化解析器
是用的我们自己写的@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}