浅谈Thymeleaf之Layout

一般来说,网站会有一些共享的常见的页面组件,如页眉,页脚,菜单等,我们可以使用布局来共享它们, Thymeleaf 很好的支持了这种功能,不需要依赖其他布局引擎,例如: Apache Tiles

本文内容基于 Thymeleaf3.0

Thymeleaf 有两种主要的布局方式,各有优势,配合使用更佳。

布局方式 说明 优劣性
包含式布局(include style) Thymeleaf标准布局。通过在每个视图中直接嵌入公共页面组件代码来生成页面。 使用简单,但不利于维护。
分层式布局(hierarchical style) 由布局方言提供支持。模板通常使用父子关系创建,从一般的部分(布局)到具体的部分(子视图;例如页面内容) 模块化维护简单,但配置更复杂。

标准布局系统

包含式布局★

包含式布局 ,也称之为 标准布局 ,十分简单,先定义模板片段,再引用片段即可。

定义片段

定义片段需要使用 th:fragment ,片段只是 th:fragment 所在标签及子标签内容。所有片段都可以在一个文件中定义或在单独的文件中定义。

当前片段位于 classpath:templates/fragments/footer.html

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>

<p th:fragment="copyright">
&copy; 2011 The Good Thymes Virtual Grocery
</p>

</body>
</html>

引用片段

引用片段语法: th:引用方式="~{templatename :: selector}"

片段表达式( ~{} )是在 Thymeleaf 3.0 引入的,为向上兼容,引用片段时可以不使用 ~{} 包含。

语法 说明
引用方式 Thymeleaf 提供了三种 引用方式
片段模板名称( templatename 可以是文件, this 或 没有关键字。
片段选择器( selector 片段名称 或 标记选择器
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>

...

<div th:insert="fragments/footer :: copyright"></div>

</body>
</html>

作用效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>

...

<div>
<p>
&copy; 2011 The Good Thymes Virtual Grocery
</p>
</div>

</body>
</html>

片段表达式

片段表达式 可以将片段做对象使用,其有三种格式。

~{templatename::selector}

templatename : 模板名称,包含了相对模板根路径的位置。

selector : 标记选择器,可以只是片段名称,也可以使用完善的 标记选择器语法

由于标记选择器的强大,甚至可以使用 id选择器( #id )直接选择片段,而不需要 th:fragment

~{templatename}

th:insert / th:replace 标签中使用必须由模板解析器解析。

~{::selector} / ~{this::selector}

优先从当前模板查找片段,若没找到,会从模板根路径开始遍历查找匹配。

引用方式

Thymeleaf 提供了三种片段引用方式。

片段引用方式 说明
th:insert 插入指定的片段到引用标签中。
th:replace 使用指定的片段替换引用的标签。
th:include 只插入片段内容(不包括标签)到引用标签中, v3.0后不推荐使用

下面通过示例,感受一下三种方式的区别

定义片段,当前片段位于 classpath:templates/fragments/footer.html

1
2
3
<p th:fragment="copyright">
&copy; 2011 The Good Thymes Virtual Grocery
</p>

使用三种方式引用片段。

1
2
3
4
5
6
7
8
9
10
11
<body>

...

<div th:insert="fragments/footer :: copyright"></div>

<div th:replace="fragments/footer :: copyright"></div>

<div th:include="fragments/footer :: copyright"></div>

</body>

三种引用方式的作用效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>

...

<div>
<p>
&copy; 2011 The Good Thymes Virtual Grocery
</p>
</div>

<p>
&copy; 2011 The Good Thymes Virtual Grocery
</p>

<div>
&copy; 2011 The Good Thymes Virtual Grocery
</div>

</body>

更多特性

Thymeleaf 模板布局还拥有很多特性和高级应用,这里列举了一些以供参考。

  • 参数化片段 : 模板片段可以向函数一样传递参数。
  • 灵活布局 : 参数化片段 + 片段表达式 = 灵活布局
  • 布局继承 : 灵活布局的利用方案,理论上可以直接实现第三方的 布局方言 的分层布局功能。

布局方言

布局方言 实际上是一个基于 Thymeleaf方言 的第三方扩展包,可以轻松的完成分层式布局。

Spring Boot 1.x 中包含了布局方言,但在 Spring Boot 2.x 中被移除。

1
2
3
4
5
6
<!-- https://github.com/ultraq/thymeleaf-layout-dialect -->
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>2.0.5</version>
</dependency>

可以在页面中引入命名空间,使 IDE 更好的支持。

1
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">

分层式布局★

分层式布局需要使用 布局方言 (Layout Dialect)。

定义布局装饰器

可以定义一个通用的布局样式(父页面),使用 layout:fragment 定义布局片段(子页面占位符)。

当前片段位于 classpath:templates/layout/default.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<body>
<!--/* 标准布局 可以和 布局方言 混合使用 */-->
<div th:replace="fragments/header :: header"></div>
<div class="container">
<!--/* 当前标签会被替换,类似th:replace */-->
<div layout:fragment="content"></div>
</div>
<div th:replace="fragments/footer :: footer"></div>
</body>
</html>

引用装饰器

在需要被装饰的页面(子页面),使用 layout:decorate 引用装饰器,并使用 layout:fragment 设置布局片段的具体内容。

子页面并不是被简单的 th:replace<head></head> 的内容也会一并被合理带入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layout/default}">
<head>
<meta charset="UTF-8">
<title>Springboot - thymeleaf</title>
</head>
<body>
<div layout:fragment="content" title="布局成功">
<div> ... 真实的内容 ... </div>
<!--/* 在子页面依然可以使用布局 */-->
<div data-th-insert="thymeleaf/common :: #commonT"></div>
</div>
</body>
</html>

方言扩展

布局方言 不仅提供了分层式布局方法,还扩展了标准布局的语法,可以直接替代标准布局方言。例如: layout:fragment , layout:include 等。具体参考 官方文档