thymeleaf 语法
thymeleaf 是什么
Thymeleaf 是一个现代的服务器端 Java 模板引擎,既可以用于 Web 环境,也能在独立环境中运行。它支持处理 HTML、XML、JavaScript、CSS 甚至纯文本文件。
简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。
从代码层次上讲:Thymeleaf是一个java 类库,他是一个xml/xhtml/html5的模板引擎,可以作为mvc的web应用的view层;
Thymeleaf 的核心目标是提供一种优雅、易维护的模板创建方式。它采用"自然模板"的设计理念,能够将业务逻辑巧妙地融入模板文件中,而不会影响模板作为设计原型的功能。这样一来,设计师和开发者之间的协作更加顺畅,有效缩小了两个团队之间的鸿沟。
Thymeleaf 从设计之初就严格遵循 Web 标准,特别是 HTML5 规范,让你可以创建完全符合标准的模板。
为什么要使用Thymeleaf
使用jsp的弊端
- 项目目录结构繁琐
- 页面不简洁
- jsp内置错误页面不能覆盖springboot默认的错误页面
- 只能打成war不能打成jar
- 内置的jetty服务器不支持jsp
Thymeleaf的优点:
- 开箱即用,它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、改jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言;
- Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
- 有网无网的情况下模版页面都可以执行,美工的页面拿来就可以用,相对jsp减少了额外的标签,页面也更加简洁。
注意:Spring-boot支持FreeMarker、Thymeleaf、jsp、veocity 。但是对freemarker和thymeleaf的支持最好,不推荐使用jsp
具体支持类型
开箱即用,Thymeleaf 支持六种不同的模板类型,每种类型被称为一种"模板模式":
1.html
2.xml
3.text
4.JavaScript
5.css
6.raw
有两种“标记”模板模式(HTML 和 XML),三种“文本”模板模式(TEXT、JAVASCRIPT 和 CSS)和一种“无操作”模板模式(RAW)。
HTML 模板模式支持任何类型的 HTML 输入,包括 HTML5、HTML 4 和 XHTML。系统不会进行验证或格式检查,输出时会最大程度地保持模板的原始代码结构。
XML 模板模式支持 XML 输入。使用此模式时,代码必须格式良好,比如不能有未关闭的标签、未引用的属性等。如果格式不正确,解析器会抛出异常。需要注意的是,系统不会执行 DTD 或 XML Schema 的验证。
TEXT 模板模式支持使用特殊语法创建非标记类型的模板,比如文本邮件或文档模板。值得注意的是,HTML 或 XML 模板也可以按 TEXT 模式处理,这时它们不会被当作标记解析,所有标签、DOCTYPE、注释等都会被当作普通文本处理。
JAVASCRIPT 模板模式支持在 Thymeleaf 应用中处理 JavaScript 文件。你可以像在 HTML 文件中一样在 JavaScript 文件中使用模型数据,同时还能享受 JavaScript 专有的功能,比如专门的转义处理或"自然脚本"。JAVASCRIPT 模板模式属于"文本"模式,因此使用与 TEXT 模板模式相同的特殊语法。
CSS 模板模式支持处理 Thymeleaf 应用中的 CSS 文件。和 JAVASCRIPT 模式一样,CSS 模板模式也属于"文本"模式,使用 TEXT 模板模式的特殊语法。
RAW 模板模式完全不处理模板内容。它主要用于将原始资源(如文件、URL 响应等)直接插入到正在处理的模板中。比如,你可以安全地在应用模板中包含外部的 HTML 资源,即使这些资源包含 Thymeleaf 代码,也不会被执行。
具体用法
环境配置
① jar包依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>② 在application.properties中配置thymleaf
spring.thymeleaf.mode = LEGACYHTML5这个配置不是必须的,但是spring.thymeleaf.mode的默认值是HTML5 ,其实是一个很严格的检查,改为LEGACYHTML5可以得到一个可能更友好亲切的格式要求。
源码
jar包部分源码
@ConfigurationProperties(prefix="spring.thymeleaf")publicclassThymeleafProperties{privatestaticfinalCharsetDEFAULT_ENCODING;//规则前缀publicstaticfinalStringDEFAULT_PREFIX="classpath:/templates/";//规则后缀publicstaticfinalStringDEFAULT_SUFFIX=".html";...}查看以上源码,thymeleaf的自动配置了规则前缀和后缀,所以只要我们把html页面放在calsspath:/templates/下,thymeleaf就能自动渲染。示例
Java controller层
@RequestMapping("/thymeleaf")publicStringthymeleaf(ModelMapmap){map.put("name","zhangsan");//往view视图传递的数据return"/grammar/thymeleaf";//返回的view视图,具体是返回前端页面url,多为项目前端页面路径}这里的Java controller层属于页面控制器 基于springmvc容器搭建
html view层
url路径为:templates/grammar/thymeleaf<h3>条件表达式语法</h3><div>1.<spanth:text="${name}"></span><br>2.<spanth:text="${name} ? ${name}+'学习好':'李四体育好 '"></span><br>3.<spanth:text="${name} ? ${name}+'学习好'"></span><br>4.<spanth:text="${name2} ? '李四体育好 '"></span><br></div>解析后的代码
<h3>条件表达式语法</h3><div>1.<span>zhangsan</span><br>2.<span>zhangsan学习好</span><br>3.<span>zhangsan学习好</span><br>4.<span></span><br></div>说明:
第2个表达式: name不为空时,即是true,则输出第一个值 zhangsan学习好 。
第3个表达式: name不为空时,即是true,则输出第一个值 zhangsan学习好 。
第4个表达式: name2不存在,即是false,则输出 ’ ‘,即空。
从第2与第4 可以看出 a? b 相当于 a? b:’’ 。
效果分析
数据传递整体流程分为 Controller 存值 → SpringMVC 域对象存储 → Thymeleaf 模板引擎解析渲染 → 输出静态 HTML 四步
1. Controller 层:ModelMap 存值(数据封装阶段)
@RequestMapping("/thymeleaf")publicStringthymeleaf(ModelMapmap){// 往ModelMap存入键值对:key=name,value=zhangsanmap.put("name","zhangsan");// 返回逻辑视图名 /grammar/thymeleafreturn"/grammar/thymeleaf";}1.1 ModelMap 底层本质
ModelMap 实现了 Map、Model 接口,底层就是一个HashMap,作用:临时存储要传给页面的数据。
map.put(“name”, “zhangsan”):把 key=name,值zhangsan存入内存 Map;
方法返回字符串 /grammar/thymeleaf 是逻辑视图名称,交给 SpringMVC 视图解析器处理
1.2 SpringMVC 视图解析流程
控制器执行完毕,把 ModelMap 里所有键值对,全部拷贝到 Request 域对象(HttpServletRequest);
视图解析器(ThymeleafViewResolver)拼接真实页面路径:
前缀templates/ + /grammar/thymeleaf + 后缀.html
最终定位页面文件:templates/grammar/thymeleaf.html
把封装好数据的Request对象、原始 HTML 文件,一并交给 Thymeleaf 模板引擎 解析
2. Thymeleaf 模板引擎解析核心机制
Thymeleaf 是服务端渲染模板,在服务器 Java 程序内完成 HTML 替换,再把纯静态 HTML 发给浏览器,浏览器只接收最终渲染好的页面,看不到th:*语法。
2.1 域取值规则${key}
${xxx}是 Thymeleaf 内置表达式语法,从 Request 域、Model、Session 域按顺序查找 key 对应的值:
优先从当前 Request 域找 key;
找不到再去 Session 域;
都无则返回null
你的代码中 map.put(“name”,“zhangsan”)同步到 Request 域,所以 ${name} 能拿到zhangsan;页面没有name2这个 key,所以 ${name2} 取值结果为null。
3. 渲染输出阶段(服务器生成静态 HTML 返回浏览器)
Thymeleaf 遍历 HTML 所有th:*指令(th:text是文本输出指令);
执行表达式、替换页面内容;
删除所有th:开头的属性(浏览器不需要模板语法);
把处理完的纯静态 HTML 字符串,通过 Http 响应返回前端浏览器;
所以浏览器拿到的最终源码里,没有任何th:text,只有替换后的纯文本,就是你给出的解析后代码。
完整数据流链路(后端→前端全过程)
1.浏览器发起 GET 请求:http://xxx/thymeleaf
2.SpringMVC 分发请求:匹配@RequestMapping(“/thymeleaf”)进入 controller 方法
3.Controller 封装数据:map.put(“name”,“zhangsan”)存入 ModelMap
4.SpringMVC 数据迁移:ModelMap 数据复制到 HttpServletRequest 域
5.视图解析器定位页面:找到templates/grammar/thymeleaf.html
6.Thymeleaf 引擎解析
读取 Request 域数据,解析${name}、三元表达式;
替换th:text对应位置文本,移除全部 th 属性;
7.服务器返回静态 HTML:渲染完成的纯 HTML 作为响应体发给浏览器
浏览器展示页面:只执行静态 HTML,无任何模板语法残留
补充关键知识点
Model / ModelMap / ModelAndView 传值区别(都是往页面传数据)
ModelMap:你当前使用,底层 Map,put(key,value)存值;
Model:参数注入,model.addAttribute(“name”,“zhangsan”);
ModelAndView:同时封装数据 + 视图名
@RequestMapping("/thymeleaf")publicModelAndViewthymeleaf(){ModelAndViewmv=newModelAndView("/grammar/thymeleaf");mv.addObject("name","zhangsan");returnmv;}三者底层最终都会把数据存入Request域,Thymeleaf${key}都能读取
常用语法 th属性
html有的属性,Thymeleaf基本都有,而常用的属性大概有七八个。其中th属性执行的优先级从1~8,数字越低优先级越高。
- th:text:设置当前元素的文本内容,相同功能的还有th:utext,两者的区别在于前者不会转义html标签,后者会。优先级不高:order=7
- th:value:设置当前元素的value值,类似修改指定属性的还有th:src,th:href。优先级不高:order=6
- th:each:遍历循环元素,和th:text或th:value一起使用。注意该属性修饰的标签位置,详细往后看。优先级很高:order=2
- th:if:条件判断,类似的还有th:unless,th:switch,th:case。优先级较高:order=3
- th:insert:代码块引入,类似的还有th:replace,th:include,三者的区别较大,若使用不恰当会破坏html结构,常用于公共代码块提取的场景。优先级最高:order=1
- th:fragment:定义代码块,方便被th:insert引用。优先级最低:order=8
- th:object:声明变量,一般和*{}一起配合使用,达到偷懒的效果。优先级一般:order=4
- th:attr:修改任意属性,实际开发中用的较少,因为有丰富的其他th属性帮忙,类似的还有th:attrappend,th:attrprepend。优先级一般:order=5
使用Thymeleaf属性需要注意点以下五点:
- 若要使用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标签上。若你将th:each属性放在div上,则循环的是将整个div。
- 变量表达式中提供了很多的内置方法,该内置方法是用#开头,请不要与#{}消息表达式弄混。
- th:insert,th:replace,th:include 三种插入代码块的效果相似,但区别很大。
案例
1.在controller中绑定数据
@ControllerpublicclassThController{@RequestMapping("/success")publicStringsuccess(Modelmodel){//1.绑定一个字符串model.addAttribute("msg","this is a <b>String</b>");model.addAttribute("msgUtext","this is a <b>String</b>");//2.绑定一个pojo对象--先去创建一个EmpEmpemp=newEmp("张三",20);model.addAttribute("emp",emp);//3.绑定一个listListlist=newArrayList();list.add(emp);list.add(newEmp("李四",20));list.add(newEmp("王五",20));model.addAttribute("emps",list);//4.绑定一个mapMap<String,Object>map=newHashMap<>();map.put("Boss",newEmp("boss",30));model.addAttribute("map",map);//前缀:classpath:/templates/ success 后缀.htmlreturn"success";}}2. 在html中使用thmeleaf语法获取数据
2.1 先声明命名空间
<!DOCTYPEhtml><htmllang="en"xmlns:th="http://www.thymeleaf.org"><head><metacharset="UTF-8"><title>Title</title></head><body><h1>this is success.html</h1></body></html>2.2 th:text和th:value
...<body><h1>this is success.html</h1><!--th:text 设置当前元素的文本内容,常用,优先级不高--><pth:text="${msg}"></p><pth:utext="${msgUtext}"></p><!--th:value 设置当前元素的value值,常用,优先级仅比th:text高-->姓名:<inputtype="text"th:value="${emp.name}"/>年龄:<inputtype="text"th:value="${emp.age}"/></body>...2.3 th:each
...<body><h1>this is success.html</h1><!--th:each 遍历列表,常用,优先级很高,仅此于代码块的插入,遍历被修饰的元素--><tableborder="1px"th:width="200px"><thead><tr><th>编号</th><th>姓名</th><th>年龄</th><th>操作</th></tr></thead><tbody><trth:each="e:${emps}"><td>编号</td><tdth:text="${e.name}"></td><tdth:text="${e.age}"></td><td>删除/修改</td></tr></tbody><tbody><trth:each="e,eState:${emps}"><tdth:text="${eState.index+1}"></td><tdth:text="${e.name}"></td><tdth:text="${e.age}"></td><td>删除/修改</td></tr></tbody></table>...说明:
th:each=“e,eState : ${emps}”
其中e为循环的每一项,eState是下标属性(可省略),eState属性包括:
index:列表状态的序号,从0开始; count:列表状态的序号,从1开始; size:列表状态,列表数据条数; current:列表状态,当前数据对象 even:列表状态,是否为奇数,boolean类型 odd:列表状态,是否为偶数,boolean类型 first:列表状态,是否为第一条,boolean类型 last:列表状态,是否为最后一条,boolean类型2.4 th:if
<!--th:if 条件判断,类似的有th:switch,th:case,优先级仅次于th:each,--> <p th:text="${map.Boss.name}" th:if="${map.Boss.age gt 20}"></p>其中关系运算:
gt:great than(大于) ge:great equal(大于等于) eq:equal(等于) lt:less than(小于) le:less equal(小于等于) ne:not equal(不等于),是否为奇数,boolean类型
odd:列表状态,是否为偶数,boolean类型
first:列表状态,是否为第一条,boolean类型
last:列表状态,是否为最后一条,boolean类型
##### 2.4 th:if```其中关系运算:
gt:great than(大于) ge:great equal(大于等于) eq:equal(等于) lt:less than(小于) le:less equal(小于等于) ne:not equal(不等于)