视图与模板引擎
⚠️ 先搞清楚你的项目需不需要视图
目前 Spring Boot 的典型场景:
| 场景 | 推荐方案 | 视图是否需要 |
|---|---|---|
| REST API(移动端/前端分离) | @RestController + JSON | ❌ 不需要 |
| 服务端渲染 Web(传统 MVC) | @Controller + Thymeleaf | ✅ 需要 |
| 纯后端(微服务) | @RestController + JSON | ❌ 不需要 |
大部分新项目选择前后端分离,只需要 @RestController 返回 JSON,完全不需要视图引擎。本页仅针对少数需要服务端渲染的场景。
Thymeleaf(对应 Blade)
Thymeleaf 是 Spring Boot 官方推荐的模板引擎。
配置
yaml
# application.yml
spring:
thymeleaf:
prefix: classpath:/templates/ # 模板目录(类比 resources/views/)
suffix: .html # 文件后缀
cache: false # 开发关闭缓存(类似 Blade 的实时编译)模板文件
html
<!-- src/main/resources/templates/home.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="${title}">Default Title</title>
<!-- ⬆ 类比 Blade: {{ $title }} -->
</head>
<body>
<h1 th:text="${title}">Welcome</h1>
<ul>
<li th:each="user : ${users}" th:text="${user.name}">
<!-- ⬆ 类比 Blade: @foreach($users as $user) {{ $user->name }} -->
</li>
</ul>
<div th:if="${users.isEmpty()}">
<!-- ⬆ 类比 Blade: @if($users->isEmpty()) -->
No users found.
</div>
</body>
</html>Controller
java
@Controller // 注意:不是 @RestController
public class HomeController {
@GetMapping("/")
public String home(Model model) {
// 类比 return view('home', ['title' => 'Hello', 'users' => $users]);
model.addAttribute("title", "Hello Spring");
model.addAttribute("users", userService.findAll());
return "home"; // 对应 templates/home.html
}
}Thymeleaf vs Blade 核心差异
| 特性 | Blade | Thymeleaf |
|---|---|---|
| 语法 | {{ $var }} | ${var} |
| 循环 | @foreach($items as $item) | th:each="item : ${items}" |
| 条件 | @if($condition) | th:if="${condition}" |
| 模板继承 | @extends('layout') | th:replace / th:insert |
| 可在模板中写 PHP 代码 | ✅ 可以写任意 PHP | ❌ 不可以 |
| 标签写法 | PHP 语法扩展 | HTML 属性(th:*) |
| 静态原型 | ❌ | ✅ 可直接用浏览器打开 |
Thymeleaf 最大的特点是"天然 HTML":
th:*属性写在 HTML 标签上,直接用浏览器打开 HTML 文件也能看到占位内容(因为th:text只是属性值,不显示在浏览器中)。Blade 的{{ }}直接在浏览器打开会报错。
布局与片段
定义布局
html
<!-- templates/layout/default.html -->
<!DOCTYPE html>
<html th:fragment="layout (title, content)">
<head>
<title th:replace="${title}">Default</title>
</head>
<body>
<header>Header</header>
<div th:replace="${content}"></div>
<footer>Footer</footer>
</body>
</html>使用布局
html
<!-- templates/user/list.html -->
<!DOCTYPE html>
<html th:replace="~{layout/default :: layout(
title='用户管理',
content=~{::main}
)}">
<body>
<main>
<h1>用户列表</h1>
<table>...</table>
</main>
</body>
</html>⚠️ Thymeleaf 的布局语法比 Blade 复杂得多。Blade 的
@extends('layout')+@section('content')更直观。如果是重度服务端渲染项目,Thymeleaf 需要花时间适应。
⚠️ 常见坑
1. 不要用 @RestController 返回视图
@RestController自带@ResponseBody,方法返回值会被 JSON 序列化而不是解析为模板名。返回的是 JSON 字符串而不是 HTML。正确:用
@Controller。
2. 模板修改后需要重启才能看到效果?
开发环境下
spring.thymeleaf.cache: false即可实时生效,不需要重启。IDE 保存后刷新浏览器即可。
3. Thymeleaf 中不能调用任意 Java 方法
Blade 中可以写
{{ $user->created_at->format('Y-m-d') }},Thymeleaf 中只能使用模板内置的工具对象:html<span th:text="${#dates.format(user.createdAt, 'yyyy-MM-dd')}"></span>或者先在 Controller 中格式化好。
4. 路径问题
html<!-- JavaScript/CSS 放在 src/main/resources/static/ 下 --> <link th:href="@{/css/app.css}" rel="stylesheet" /> <script th:src="@{/js/app.js}"></script> <!-- @{} 语法自动处理上下文路径 -->