SpringBoot入门建站全系列(八)集成模板引擎(thyme
SpringBoot入门建站全系列(八)集成模板引擎(thymeleaf)渲染页面
说到页面渲染,这里不得不给大家科普一下:
- html文件负责显示页面,后台数据可以通过ajax方式获取,如果数据完全使用ajax获取,这样页面和后端就没什么联系,这时候就可以成为前后端分离。
- jsp页面是一种页面渲染方式,它是对jsp文件解析后,将后台数据填充到html页面并返回到前端,这种方式是非前后端分离的,所以,脱离了容器,jsp页面啥都不是,根本打不开。jsp可以写java代码,可以写逻辑,功能很强大,但是也是它逐渐被抛弃的原因。
- 模板引擎, 目前是比较流行的一种写法,或许你会问:前后端分离已经很好用了,为啥还要用模板引擎?原因是这样的,前后端分离的页面,是不经过容器控制的,所以安全框架不能对它进行过滤,这是其一,其二、前后端分离的页面,数据是ajax异步获取的,所以首次打开页面时(比如爬虫爬取页面,未执行js等),获取到的只是一些html元素,没有数据填充,而模板引擎可以在获取html文件时就将数据填充进去。
- 模板引擎也是依赖于后端容器,页面中的内容使用标签进行替换。
本文不讲前后端分离,先讲下模板引擎,Springboot支持很多模板引擎,thymeleaf算是比较好用的一种。
项目地址:
品茗IT-同步发布
品茗IT 提供在线支持:
一、Maven依赖
需要引入spring-boot-starter-thymeleaf和spring-boot-starter-web。
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cff</groupId>
<artifactId>springbootwork</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>Thymeleaf</artifactId>
<name>Thymeleaf</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
</project>
父pom管理了所有依赖jar包的版本,地址: https://www.pomit.cn/spring/SpringBootWork/pom.xml
二、thymeleaf配置
SpringBoot对thymeleaf做了很好的整合,隐藏了大部分细节,所以,我们只需要在Springboot的配置文件(一般是用application.properties)中加入以下配置即可:
spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.content-type=text/html
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
这里,
-
spring.thymeleaf.cache是对页面不缓存,这样修改了模板文件,立即生效。
-
spring.thymeleaf.prefix指明了模板文件的地址
-
spring.thymeleaf.suffix 指明了模板文件后缀。
另外三个参数就不说了,顾名思义。
三、thymeleaf的模板页面
这里建了一个模板文件detail.html文件。乍一看,它和html没啥区别,其实区别真不大,只是里面有thymeleaf的标签。
里面的th:开头的都是thymeleaf的标签,如th:href、th:onclick、th:text等。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="keywords" th:attr="content=${data.data.catory}"/>
<meta name="description" th:attr="content=${data.data.title}"/>
<title th:text="${data.data.title}">品茗IT-博文详情</title>
<!-- CSS -->
<link href="https://lib.baomitu.com/material-design-icons/3.0.1/iconfont/material-icons.min.css" rel="stylesheet">
<link href="https://lib.baomitu.com/materialize/0.100.2/css/materialize.min.css" type="text/css" rel="stylesheet" media="screen,projection"/>
<script src="https://lib.baomitu.com/jquery/3.3.0/jquery.min.js"></script>
<style>
nav ul a,
nav .brand-logo {
color: #444;
}
.side-nav{
max-width: 50%;
}
p {
line-height: 2rem;
}
.button-collapse {
color: #26a69a;
}
.parallax-container {
min-height: 380px;
line-height: 0;
height: auto;
color: rgba(255,255,255,.9);
}
.parallax-container .section {
width: 100%;
}
@media only screen and (max-width : 992px) {
.parallax-container .section {
position: absolute;
top: 40%;
}
#index-banner .section {
top: 10%;
}
}
@media only screen and (max-width : 600px) {
#index-banner .section {
top: 0;
}
.collection .collection-item.avatar {
padding-left: 15px;
}
}
.icon-block {
padding: 0 15px;
}
.icon-block .material-icons {
font-size: inherit;
}
footer.page-footer {
margin: 0;
}
.token.punctuation {
color: #999;
}
pre {
-moz-osx-font-smoothing: initial;
-webkit-font-smoothing: initial;
background-color: #f8f8f8;
font-family: 'Roboto Mono', Monaco, courier, monospace;
line-height: 1.5rem;
margin: 1.2em 0;
overflow: auto;
padding: 0 1.4rem;
position: relative;
word-wrap: normal;
}
pre > code {
-moz-osx-font-smoothing: initial;
-webkit-font-smoothing: initial;
background-color: #f8f8f8;
border-radius: 2px;
color: #525252;
display: block;
font-family: 'Roboto Mono', Monaco, courier, monospace;
font-size: 0.8rem;
line-height: inherit;
margin: 0 2px;
max-width: inherit;
overflow: inherit;
padding: 2.2em 5px;
white-space: inherit;
}
.dialog{
position: relative;
display: inline-block;
max-width: 200px;
padding: 4px 8px;
border-radius: 2px;
background-color: #dddddd;
line-height: 20px;
font-size: 14px;
}
.u-tri:before{
position: absolute;
left: 77px;
top: 11px;
content: '';
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 12px solid transparent;
border-left: 12px solid #dddddd;
}
.question{
font-size: 22px;
font-weight: bold;
color: #112f6b;
}
.question-index{
font-size: 10px;
color: grey;
}
.btn{
padding: 0 1rem;
color:#2196f3 ;
}
.authorName{
padding-left:10px;
font-size: 14px;
color: #64b5f6 ;
cursor: pointer;
}
.createTime{
padding-left:10px;
font-size: 12px;
color: grey;
}
.catory{
padding-left:5px;
font-size: 14px;
color: grey;
}
.catory:before{
content:"【";
}
.catory:AFTER{
content:"】";
}
.outer {
overflow: hidden;
text-overflow: ellipsis;
/*设置成弹性盒子 */
display: -webkit-box;
/*显示的个数 */
-webkit-line-clamp: 2;
/* 属性规定框的子元素应该被水平或垂直排列。 */
-webkit-box-orient: vertical;
}
.editBtn{
padding-right:40px;
}
.form-control {
display: block;
width: 100%;
height: 50px;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
color: #555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
-webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
-o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
}
.comment-form{
height: 100px;
}
.comments-list-area{
padding: 20px 10px;
}
.comments-area{
padding: 20px 10px;
}
.important-text{
font-weight: bold;
color:#112f6b;
}
.replyCommentBtn{
margin-left:10px;
color:light-blue;
}
.replyCommentBtn:hover{
color:#1565c0;
}
.fixed-menu{
position: relative;
}
</style>
</head>
<body class="amber lighten-5">
<!-- header end -->
<div class="fixed-menu hide-on-med-and-down">
<ul id="tree" class="ztree" style='width:100%'>
</ul>
</div>
<!-- content -->
<div class="section no-pad-bot " >
<div class="container">
<div class="card-panel" style="min-height:90vh;padding: 0px">
<ul class="collection" id="issueList" style="border: none">
<li class="collection-item avatar">
<div class="row">
<div class="col s12"><span class="question" id="title" th:text="${data.data.title}">Spring序列化布尔类型错误</span></div>
</div>
<div class="row">
<span class="catory" id="catory" th:text="${data.data.catory}">【软件使用】</span>
<span class="createTime" id="createTime" th:text="${#dates.format(data.data.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-03-06</span>
<span class="createTime" id="openStatus" th:text="${data.data.open}==0?'公开':'私有'">未知</span>
<a class="authorName" id="authorName" th:href="'/page/userData.html?user=' + ${data.data.author}" th:text="${data.data.author}">匿名</a>
<span style="padding-left:20px;" class="createTime">点赞:<span class="starNum" id="starNum" th:text="${data.data.star}">1</span></span>
<a id="editBtn" class="editBtn right" th:href="'/page/issue/editIssue.html?id=' + ${data.data.id}" th:if="${#strings.equals(data.remark,data.data.author)}">编辑</a>
</div>
<div class="divider"></div>
<div class="row" style="margin-top: 2rem;">
<div class="col s12">
<span class="answer markdown-section" id="content" ></span>
</div>
</div>
</li>
</ul>
<div class="divider"></div>
<div class="center" style="padding-top: 20px;;left: 45%;padding-bottom: 20px;">
<a id="starBtn" th:onclick="'javascript:issueStar(' + ${data.data.id} + ');'" class="waves-effect waves-light btn light-blue lighten-5"><i id="star_icon" class="material-icons right">favorite_border</i>赞</a>
</div>
</div>
</div>
</div>
<!-- footer start -->
<footer class="page-footer teal">
<div class="container">
<div class="row">
<div class="col l8 s12">
<h5 class="white-text">网站简介.</h5>
<p class="grey-text text-lighten-4">
本站提供多领域的技术解决方案,包括web网站、小程序、开源项目、h5等。
</p>
</div>
</div>
</div>
<div class="footer-copyright">
<div class="container">
Copyright (c) 2018-present, 陈付菲.
<a class="grey-text text-lighten-4 right" ></a>
</div>
</div>
</footer>
<!-- footer end -->
</body>
<script src="https://lib.baomitu.com/materialize/0.100.2/js/materialize.min.js"></script>
<script th:inline="javascript">
$(function () {
});
</script>
</html>
四、thymeleaf的页面控制器
使用模板引擎,需要我们自己控制ModelAndView。这里的ModelAndView的viewName是detail,结合thymeleaf的配置,就是找spring.thymeleaf.prefix + viewName + spring.thymeleaf.suffix 指定的这个文件。
package com.cff.springbootwork.thymeleaf.web;
import java.util.Date;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import com.cff.springbootwork.thymeleaf.dto.FQuestionInfo;
import com.cff.springbootwork.thymeleaf.dto.ResultCode;
import com.cff.springbootwork.thymeleaf.dto.ResultModel;
@RestController
@RequestMapping("/thymeleaf")
public class ThymeleafRest {
@RequestMapping("/page")
public <FQuestionIndex> ModelAndView getThymeleaf() {
ModelAndView modelAndView = new ModelAndView("detail");
try {
FQuestionInfo fQuestionInfo = new FQuestionInfo();
fQuestionInfo.setAuthor("cff");
fQuestionInfo.setCatory("大爷");
fQuestionInfo.setId(123123123L);
fQuestionInfo.setOpen(1);
fQuestionInfo.setStar(123);
fQuestionInfo.setTitle("我就是一个测试模板引擎的实体而已。");
fQuestionInfo.setCreateTime(new Date());
ResultModel retOk = ResultModel.ok(fQuestionInfo);
modelAndView.addObject("data", retOk);
} catch (Exception e) {
e.printStackTrace();
modelAndView.addObject("data", new ResultModel(ResultCode.CODE_00004));
}
return modelAndView;
}
}
这里面,FQuestionInfo 是保存数据的一个实体,ResultModel是一个统一返回的实体,其实可以写成一个实体,都是作为返回数据的。
modelAndView使用addObject("data", retOk);将数据传递给viewName指定的模板文件,解析后返回一个html文件给浏览器。
页面如下:
在这里插入图片描述五、过程中使用到的实体
详细完整的实体,可以访问品茗IT-博客《SpringBoot入门建站全系列(八)集成模板引擎(thymeleaf)渲染页面》
快速构建项目
喜欢这篇文章么,喜欢就加入我们一起讨论SpringBoot技术吧!
品茗IT交流群