八、SpringBootWeb开发-模板引擎
- 由于 SpringBoot 使用了嵌入式 Servlet 容器。所以 JSP 默认是不能使用的。
- 如果需要服务端页面渲染,优先考虑使用模板引擎。
模板引擎页面默认放在 src/main/resources/templates
SpringBoot 包含以下模板引擎的自动配置
- FreeMarker
- Groovy
- Thymeleaf
- Mustache
Thymeleaf官网:https://www.thymeleaf.org/
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all" th:href="@{/css/gtvg.css}" />
</head>
<body>
<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
</body
</html>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 1. Thymeleaf整合
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
1
2
3
4
2
3
4
自动配置原理
开启了
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration
自动配置属性绑定在
ThymeleafProperties
中,对应配置文件spring.thymeleaf
内容所有的模板页面默认在
classpath:/templates
文件夹下默认效果
所有的模板页面在
classpath:/templates/
下面找找后缀名为
.html
的页面
# 2. 基础语法
# 1. 核心用法
th:xxx
:动态渲染指定的 html 标签属性值、或者th指令(遍历、判断等)
th:text
:标签体内文本值渲染
th:utext
:不会转义,显示为html原本的样子,写的html标签以及css效果都会显示在浏览器上
th:属性
:标签指定属性渲染th:attr
:标签任意属性渲染th:if
th:each
...
:其他th指令- 例如:
<p th:text="${content}">原内容</p>
<a th:href="${url}">登录</a>
<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
1
2
3
4
2
3
4
表达式
:用来动态取值
${}
:变量取值;使用model共享给页面的值都直接用${}**
@{}
:url路径;**可以自动匹配项目路径,防止项目路径改变后需要手动修改<img style="width:300px;" th:src="@{/static/2.jpg}"> <!--这样写的话如果项目根路径为/demo,那么这个src匹配的值为/demo/static/2.jpg--> <!--另外,还可以用下面这种嵌套写法--> <img th:src="@{${imgUrl}}" th:style="${style}" th:if="${show}">
1
2
3
4#{}
:国际化消息~{}
:片段引用*{}
:变量选择:需要配合th:object绑定对象
系统工具&内置对象:详细文档 (opens new window)
param
:请求参数对象session
:session对象application
:application对象#execInfo
:模板执行信息#messages
:国际化消息#uris
:uri/url工具#conversions
:类型转换工具#dates
:日期工具,是java.util.Date
对象的工具类#calendars
:类似#dates,只不过是java.util.Calendar
对象的工具类#temporals
: JDK8+**java.time**
API 工具类#numbers
:数字操作工具#strings
:字符串操作#objects
:对象操作#bools
:bool操作#arrays
:array工具#lists
:list工具#sets
:set工具#maps
:map工具#aggregates
:集合聚合工具(sum、avg)#ids
:id生成工具
例如:
转大写
<h1 th:text="${#strings.toUpperCase(name)}"></h1>
1
2
2
# 2. 语法示例
表达式:
- 变量取值:${...}
- url 取值:@{...}
- 国际化消息:#{...}
- 变量选择:*{...}
- 片段引用: ~{...}
常见:
- 文本: 'one text','another one!',...
- 数字: 0,34,3.0,12.3,...
- 布尔:true、false
- null: null
- 变量名: one,sometext,main...
文本操作:
- 拼串: +
- 文本替换:| The name is ${name} |
<h1 th:text="${'前缀:'+name+'后缀'}"></h1>
拼串
<h1 th:text="|前缀 ${name} 后缀|"></h1>
1
2
3
2
3
布尔操作:
- 二进制运算: and,or
- 取反:!,not
比较运算:
- 比较:>,<,<=,>=(gt,lt,ge,le)
- 等值运算:==,!=(eq,ne)
条件运算:
- if-then: (if)?(then)
- if-then-else: (if)?(then):(else)
- default: (value)?:(defaultValue)
特殊语法:
- 无操作:_
所有以上都可以嵌套组合
'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))
1
# 3. 属性设置
th:href="@{/product/list}"
th:attr="class=${active}"
th:attr="src=@{/images/gtvglogo.png},title=${logo},alt=#{logo}"
th:checked="${user.active}"
<p th:text="${content}">原内容</p>
<a th:href="${url}">登录</a>
<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
1
2
3
4
2
3
4
# 4. 遍历
语法: th:each="元素名,迭代状态 : ${集合}"
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
iterStat 有以下属性:
- index:当前遍历元素的索引,从0开始
- count:当前遍历元素的索引,从1开始
- size:需要遍历元素的总数量
- current:当前正在遍历的元素对象
- even/odd:是否偶数/奇数行
- first:是否第一个元素
- last:是否最后一个元素
# 5. 判断
# th:if
<a
href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}"
>view</a
1
2
3
4
5
2
3
4
5
# th:switch
<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>
1
2
3
4
5
2
3
4
5
# 6. 属性优先级
- 片段
- 遍历
- 判断
<ul>
<li th:each="item : ${items}" th:text="${item.description}">Item description here...</li>
</ul>
1
2
3
2
3
以下优先级从高到低排序
Order | Feature | Attributes |
---|---|---|
1 | 片段包含 | th:insert th:replace |
2 | 遍历 | th:each |
3 | 判断 | th:if th:unless th:switch th:case |
4 | 定义本地变量 | th:object th:with |
5 | 通用方式属性修改 | th:attr th:attrprepend th:attrappend |
6 | 指定属性修改 | th:value th:href th:src ... |
7 | 文本值 | th:text th:utext |
8 | 片段指定 | th:fragment |
9 | 片段移除 | th:remove |
# 7. 行内写法
[[...]] or [(...)]
<p>Hello, [[${session.user.name}]]!</p>
1
2
2
# 8. 变量选择
可以用th:object
绑定一个对象,下面使用 *
就可以快捷使用
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>
1
2
3
4
5
2
3
4
5
等同于
<div>
<p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div
1
2
3
4
5
2
3
4
5
# 9. 模板布局
- 定义模板:
th:fragment
- 引用模板:
~{templatename::selector}
- 插入模板:
th:insert
、th:replace
<footer th:fragment="copy">© 2011 The Good Thymes Virtual Grocery</footer>
<body>
<div th:insert="~{footer :: copy}"></div>
<div th:replace="~{footer :: copy}"></div>
</body>
<body>
结果:
<body>
<div>
<footer>© 2011 The Good Thymes Virtual Grocery</footer>
</div>
<footer>© 2011 The Good Thymes Virtual Grocery</footer>
</body>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 10. devtools
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
1
2
3
4
2
3
4
修改页面后;ctrl+F9
刷新效果;
java代码的修改,如果devtools
热启动了,可能会引起一些bug,难以排查
编辑 (opens new window)
上次更新: 2024/05/30, 07:49:34