伺服器渲染技術-Thymeleaf 1.基本介紹 官方線上文檔:Read online 文檔下載:Thymeleaf 3.1 PDF, EPUB, MOBI Thymeleaf 是什麼 Thymeleaf是一個現代的伺服器端Java模板引擎,適用於Web和獨立環境,能夠處理HTML,XML,Java ...
伺服器渲染技術-Thymeleaf
1.基本介紹
官方線上文檔:Read online 文檔下載:Thymeleaf 3.1 PDF, EPUB, MOBI
Thymeleaf 是什麼
-
Thymeleaf是一個現代的伺服器端Java模板引擎,適用於Web和獨立環境,能夠處理HTML,XML,JavaScript,CSS甚至純文本
-
Thymeleaf 是一個跟 Velocity、FreeMarker 類似的模板引擎,可完全替代 JSP
-
Thymeleaf 是一個 Java 類庫,是一個xml/ xhtml/ html5的模板引擎,可以作為 mvc 的 web 應用的 view 層
Thymeleaf 的優點
- 實現 JSTL、OGNL 表達式效果,語法類似,上手快
- Thymeleaf 模板頁面無需伺服器渲染,也可以被瀏覽器運行,頁面簡潔
- SpringBoot 支持 FreeMarker、Thymeleaf、Veocity
Thymeleaf 的缺點
- Thymeleaf 並不是一個高性能的引擎,適用於單體應用
- 如果要做一個高併發的應用,選擇前後分離更好,比如 Vue + SpringBoot
2.Thymeleaf機制
Thymeleaf 是伺服器渲染技術,頁面數據是在服務端進行渲染的
例如:某個頁面被請求,其中有一段Thymeleaf代碼,則 thymeleaf 模板引擎完成處理之後(在服務端完成),才會將頁面結果返回。因此使用Thymeleaf,並不是前後端分離。
3.語法
3.1表達式
1.表達式一覽
表達式名稱 | 語法 | 用途 |
---|---|---|
變數取值 | ${...} |
獲取請求域、session域、對象等值 |
選擇變數 | *{...} |
獲取上下文對象值 |
消息 | #{...} |
獲取國際化等值 |
鏈接 | @{...} |
生成鏈接 |
片段表達式 | ~{...} |
jsp:include 作用,引入公共頁面片段 |
2.字面量
文本值:'jack', 'hello', ...
數字:10, 5, 36.8, ...
布爾值:true,false
空值:null
變數:name, age, ...(變數名不能有空格)
3.文本操作
字元串拼接:+
變數替換:|age=${age}|
3.2運算符
1.數學運算
運算符:+,-,*,/,%
2.布爾運算
運算符:and,or
一元運算:!,not
3.比較運算
比較:>,<,>=,<=(gt,lt,ge,le)
等式:==,!=(eq,ne)
4.條件運算
If-then:(if)?(then)
If-then-else:(if)?(then):(else)
Default:(value)?:(defaultvalue)
3.3th屬性
html有的屬性,Thymeleaf基本都有,而常用的屬性大概有七八個。其中th屬性執行的優先順序從1~8,數字越小優先順序越高。
-
th:fragment
聲明代碼塊,方便被 th:insert 引用。優先順序為 order=8
-
th:text
設置當前元素的文本內容,相同功能的還有 th:utext,兩者的區別在於前者不會轉義 html 標簽,而後者會。優先順序為 order=7
-
th:value
設置當前元素的 value 值,類似修改指定屬性的還有 th:src,th:href。優先順序為 order=6
-
th:attr
修改任意屬性,實際開發中使用較少,因為有豐富的其他th屬性幫忙,類似的還有th:attrappend,th:attrprepend。優先順序為 order=5
-
th:object
聲明變數,一般和 *{} 一起配合使用,達到偷懶效果。優先順序 order=4
-
th:if
條件判斷,類似的還有 th:unless,th:switch,th:case。優先順序為 order=3
-
th:each
遍歷迴圈元素,和 th:text 或 th:value 一起使用。註意該屬性修飾的標簽位置。優先順序為 order=2
-
th:insert
代碼塊引入,類似的還有 th:replace,th:include。三者的區別較大,若使用不當會破壞html結構,當用於公共代碼塊提取的場景,優先順序為 order=1
3.4迭代
在前端頁面中,總是出現需要遍歷集合中的元素以展示所有信息的場景。Thymeleaf標準方言為我們提供了一個有用的屬性:th:each。
假設後臺控制器添加了一個商品列表的屬性 prods。然後,我們使用 th:each 在模板中使用來遍歷產品列表:
<!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"
href="../../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
</head>
<body>
<h1>Product list</h1>
<table>
<tr>
<th>NAME</th>
<th>PRICE</th>
<th>IN STOCK</th>
</tr>
<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>
</table>
<p>
<a href="../home.html" th:href="@{/}">Return to home</a>
</p>
</body>
</html>
prod : ${prods} 屬性的含義為:迴圈 \({prods} 屬性的每一個元素。\){prods} 為被迭代變數,prod 為迭代變數,即當前迴圈的元素。
需要註意的是,prod 為迭代變數的作用域為 <tr>
元素,可用於其內部標記 <td>
。
3.5條件運算
例如:
<a href="comments.html"
th:href="@{/product/comments(prodId=${prod.id})}"
th:if="${not #lists.isEmpty(prod.comments)}">view</a>
這將創建一個指向評論頁面(帶有 URL )的鏈接,並將參數設置為產品的參數,但前提是產品有任何評論。/product/comments
prodId
id
還有一種方法可以使用 Java 中的等效開關結構有條件地顯示內容:switch-case-default
<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>
3.6註意事項
- 使用Thymeleaf語法首先要聲明名稱空間:
xmlns:th="http://www.thymeleaf.org"
- 設置文本 th:text,設置input的值用 th:value,迴圈輸出用 th:each,條件判斷用 th:if,插入代碼塊用 th:insert,定義代碼塊用 th:fragment,聲明變數用 th:object
- th:each 的用法需要格外註意,如果你要迴圈一個div中的p標簽,則 th:each 屬性必須放在p標簽上。若你將其放在div上,迴圈的將是整個div
- 變數表達式中提供了很多的內置方法,該內置方法是用#開頭,不要和#{}混淆。
4.綜合案例
- 需求:使用 SpringBoot+Thymeleaf 完成簡單的用戶登錄-列表功能。
- 如果沒有登錄就訪問管理頁面,提示非法訪問,需要登錄
- 如果登錄成功,顯示登錄名稱,以及用戶列表
- 為了簡化,這裡就不連資料庫了,使用Javabean代替數據
註意:這裡的thymeleaf的templates目錄不能直接訪問,是因為SpringBoot預設可以訪問的靜態資源路徑沒有templates,因此除了上面請求轉發到資源的方法外,也可以配置靜態資源的訪問路徑:
spring:
web:
resources:
static-locations: [classpath:/static/,classpath:/templates/]
或者在配置類中配置(略)。
4.1代碼實現
(1)創建 SpringBoot 項目,引入基本的庫文件
<!--導入SpringBoot父工程-->
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.5.3</version>
</parent>
<dependencies>
<!--導入場景啟動器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!--引入thymeleaf-starter:項目會自動完成配置,相關類會自動註入容器中-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
(2)創建 login.html 和 manage.html 和靜態圖片到指定目錄(templates目錄,該目錄不能直接訪問)
login.html
<!DOCTYPE html>
<!--引入命名空間-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登錄</title>
</head>
<body bgcolor="#CED3FE">
<div style="text-align: center">
<h1>用戶登錄</h1>
<hr/>
<img src="images/login.jpg" width="35"/>
<!--th:action="@{/login}"可以替換#-->
<form action="#" th:action="@{/login}" method="post">
<label style="color: red" th:text="${msg}"></label><br/>
用戶名:<input type="text" style="width: 150px" name="name"/><br/><br/>
密 碼:<input type="password" style="width: 150px" name="password"/><br/><br/>
<input type="submit" value="登錄"/>
<input type="reset" value="重新填寫"/>
</form>
</div>
</body>
</html>
manage.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>管理後臺</title>
</head>
<body bgcolor="#CED3FE">
<a href="#" th:href="@{/}">安全退出</a>
<!--行內取法,使用雙層中括弧-->
歡迎您:[[${session.loginAdmin.name}]]
<hr/>
<h1>管理雇員</h1>
<div style="position: center ">
<table border="1px" cellspacing="0" bordercolor="green" style="width: 700px">
<tr bgcolor="pink">
<td>id</td>
<td>name</td>
<td>age</td>
<td>pwd</td>
<td>email</td>
</tr>
<!--迴圈展示-->
<tr bgcolor="#ffc0cb" th:each="user:${users}">
<td th:text="${user.id}">a</td>
<td th:text="${user.name}">b</td>
<td th:text="${user.age}">c</td>
<td th:text="${user.password}">d</td>
<td th:text="${user.email}">e</td>
</tr>
</table>
<br/>
</div>
<hr/>
</body>
</html>
(3)Javabean
Admin.java
package com.li.thymeleaf.bean;
import lombok.Data;
/**
* @author 李
* @version 1.0
*/
@Data
public class Admin {
private String name;
private String password;
}
User.java
package com.li.thymeleaf.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 李
* @version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private Integer age;
private String password;
private String email;
}
(4)控制器Controller
IndexController
package com.li.thymeleaf.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author 李
* @version 1.0
*/
@Controller
public class IndexController {
//編寫方法,轉發到登錄頁
@GetMapping(value = {"/", "/login"})
public String login() {
//因為我們引入了starter-thymeleaf,這裡會直接
//使用視圖解析到thymeleaf模板文件下的adminLogin.html
return "adminLogin";
}
}
AdminController
package com.li.thymeleaf.controller;
import com.li.thymeleaf.bean.Admin;
import com.li.thymeleaf.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
/**
* @author 李
* @version 1.0
*/
@Controller
public class AdminController {
/**
* 響應用戶登錄請求
*
* @param admin 自定義對象參數
* @param session 將獲取的admin信息放入session中(登錄成功)
* @param model model的數據會自動放入request域中傳給下一個頁面(登錄失敗)
* @return
*/
@PostMapping("/login")//從請求方式區分,不會衝突
public String login(Admin admin, HttpSession session, Model model) {
//驗證用戶是否合法
if (StringUtils.hasText(admin.getName()) && "666".equals(admin.getPassword())) {//合法
//將登陸用戶保存到session中
session.setAttribute("loginAdmin", admin);
//應使用重定向(用請求轉發刷新頁面會重覆提交表單),這裡的重定向是到mainPage方法,而不是直接到頁面。
return "redirect:manage.html";
} else {//不合法,返回重新登錄
model.addAttribute("msg", "用戶名或密碼錯誤!");
return "adminLogin";
}
}
//處理用戶請求到manage.html
@GetMapping("/manage.html")
public String mainPage(Model model, HttpSession session) {
//先校驗(這裡暫時使用session驗證,後面可以統一使用攔截器)
Object loginAdmin = session.getAttribute("loginAdmin");
if (loginAdmin != null) {//說明登陸過
//模擬用戶數據
List<User> users = new ArrayList<>();
users.add(new User(1, "關羽", 555, "1234", "[email protected]"));
users.add(new User(2, "張飛", 455, "12345", "[email protected]"));
users.add(new User(3, "趙雲", 344, "12346", "[email protected]"));
users.add(new User(4, "馬超", 300, "12347", "[email protected]"));
users.add(new User(5, "黃忠", 666, "12348", "[email protected]"));
//放入到request域中(model中的數據會自動放入到request域中)
model.addAttribute("users", users);
return "manage";//這裡才是真正視圖解析到 templates/manage.html
} else {//說明沒有登錄過,拒絕訪問manage頁面
model.addAttribute("msg", "你沒有登錄/請登錄!");
return "adminLogin";
}
}
}
(5)啟動類
package com.li.thymeleaf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author 李
* @version 1.0
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
(6)測試
- 未經登錄直接訪問manage.html
- 登錄測試
4.2練習
-
把前面接收參數相關註解、自定義轉換器、處理JSON、內容協商相關代碼和案例過一遍
-
將Thymeleaf用戶管理改為妖怪列表,欄位做相應的改變,進行練習
- Monster [id,name,skill,age,salary,birth]