Skip to content

视图与模板引擎


⚠️ 先搞清楚你的项目需不需要视图

目前 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 核心差异

特性BladeThymeleaf
语法&#123;&#123; $var &#125;&#125;${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 的 &#123;&#123; &#125;&#125; 直接在浏览器打开会报错。


布局与片段

定义布局

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 中可以写 &#123;&#123; $user->created_at->format('Y-m-d') &#125;&#125;,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>
<!-- @{} 语法自动处理上下文路径 -->

面向 PHP 开发者的 Spring Boot 文档