1. 服务器生成动态页面的方式
这里介绍两种常用的服务器生成动态页面的方式:服务器渲染和客户端渲染
渲染就是把数据和页面结合起来
1.1 客户端渲染(前后端分离)
客户端渲染流程:
客户端渲染是在请求回数据后就开始渲染了,只是有些数据还没有拿到,故暂时没法渲染。但是大部分页面的内容是可以获取到的,因此客户端渲染可以完成页面的部分刷新。
客户端渲染示例: 淘宝某商品的评论,我们点击该商品评论区的其它页面,只是刷新了评论区域,整个商品页面并没有刷新,浏览器的网址也没有变。当我们点击评论区的下一页时,客户端就会向服务器发送 Ajax 请求,之后就会拿到该页评论信息,并对这个局部区域进行渲染
客户端渲染的特点:
- 客户端通过 Ajax 的方式和服务器进行交互
- 服务器返回的不是完整的 html,通常只是纯粹的数据,这些数据常见的以 json 的方式来组织
客户端渲染的优点:
- 能够让前后端进行充分的解耦,前后端的开发互不干扰,也能各自进行测试
- 可以向用户快速展示页面的内容,增加用户体验
- 通过爬虫爬取响应的内容会增加一定的难度
客户端渲染的缺点:
- 前后端交互的次数比较多
- 一个页面可能需要多组 Ajax 来获取完整的数据,效率稍微低一些
- 不利于 SEO 搜索引擎优化,即搜索引擎搜索不到客户端渲染的数据
1.2 服务器渲染(使用模板引擎)
服务器渲染流程:
服务器渲染是在请求后,服务器对于请求的网页文件直接渲染好,然后返回给客户端。
服务器渲染示例: 淘宝上搜索某商品,当我们通过销量、价格、信用等分类查询该商品时,整个页面都会被刷新,浏览器的网址也会改变
服务器渲染的特点:
- 服务器返回一整个 html 页面
- html 页面上的一些动态数据,往往是通过“模板引擎”的方式来进行动态替换的
服务器渲染的优点:
- 前后端交互次数比较少,一次 HTTP 请求/响应,就能够拿到一个完整的页面,比较高效
- 利于 SEO 搜索引擎优化,即能被搜索引擎搜索到,能向用户展示你网页的内容
服务器渲染的缺点:
- 前后端代码不能充分解耦合,前后端开发很难进行明确分工,不能够独立测试
- 如果数据量过大,服务器渲染的时间就会过长,造成浏览器的暂时性空白
- 页面上的数据容易被爬虫获取
2. 模板引擎
2.1 模板引擎介绍
-
模板引擎(template engine): 是为了使用户界面与业务数据分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的 HTML 文档。当服务器将模板中动态变化的部分计算好之后,将模板中的占位符给替换成计算的结果,然后将这个组装好的 HTML 格式的字符串返回给浏览器。 -
模板(template): 就是一个 HTML,只不过这个 HTML 是把一些动态变化的数据给挖空,用特殊符号代替。
2.2 模板引擎的作用
- 模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率,并且良好的设计也使得代码重用变得更加容易。
- 模板引擎不只是可以让你实现代码分离(业务逻辑代码和用户界面代码),也可以实现数据分离(动态数据与静态数据),还可以实现代码单元共享(代码重用),甚至是多语言、动态页面与静态页面自动均衡(SDE)等等与用户界面可能没有关系的功能。
2.3 常见的模板引擎
-
Thymeleaf
Thymeleaf 是新一代 Java 模板引擎,与 Velocity、FreeMarker 等传统 Java 模板引擎不同,Thymeleaf 支持 HTML 原型,其文件后缀为 .html ,因此它可以直接被浏览器打开,此时浏览器会忽略未定义的 Thymeleaf 标签属性,展示 Thymeleaf 模板的静态页面效果;当通过 Web 应用程序访问时 Thymeleaf 会动态地替换掉静态内容,使页面动态显示。
-
FreeMaker
在所有采用网页静态化手段的网站中,FreeMarker 使用的比例大大的超过了其他的一些技术。HTML 静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以使用 FreeMarker 将 HTML 静态化。比如一些网站的公用设置信息,这些信息基本都是可以通过后台来管理并存储在数据库中,这些信息其实会大量的被前台程序调用,每一次调用都会去查询一次数据库,但是这些信息的更新频率又会很小,因此也可以考虑将这部分内容进行后台更新的时候进行静态化,这样就避免了大量的数据库访问请求,提高了网站的性能。
-
Enjoy
Enjoy 是基于 Java 语言的极轻量极模板引擎且不依赖任何第三方。极简设计仅由 if、for、switch、set、define、include、render 七个核心指令,让学习成本低到极致。独创 DKFF(Dynamic Key Feature Forward)词法分析算法与 DLRD(Double Layer Recursive Descent)语法分析算法,避免使用 javacc、antlr、jflex 生成器,令代码量少到极致。
-
Velocity
Velocity 是一个基于 Java 的模板引擎。它允许任何人使用简单但功能强大的模板语言来引用 Java 代码中定义的对象。
-
JSP
JSP 虽然是一款功能比较强大的模板引擎,并被广大开发者熟悉,但它前后端耦合比较高。比如说前端的 HTML 页面还要手动修改成 JSP 页面,大大加重了工作量,而且动态和静态资源也是耦合性太高。其次是 JSP 页面的效率没有 HTML 高,因为 JSP 是同步加载。而且 JSP 需要tomcat,但又不支持 nginx 等,已经跟不上时代的潮流。
3. Thymeleaf
3.1 Thymeleaf 介绍
Thymeleaf 基本介绍:
Thymeleaf 是新一代 Java 模板引擎,与 Velocity、FreeMarker 等传统 Java 模板引擎不同,Thymeleaf 支持 HTML 原型,其文件后缀为 .html ,因此它可以直接被浏览器打开,此时浏览器会忽略未定义的 Thymeleaf 标签属性,展示 Thymeleaf 模板的静态页面效果;当通过 Web 应用程序访问时 Thymeleaf 会动态地替换掉静态内容,使页面动态显示。
Thymeleaf 的特点:
- 动静结合:Thymeleaf 既可以直接使用浏览器打开,查看页面的静态效果,也可以通过 Web 应用程序进行访问,查看动态页面效果。
- 开箱即用:Thymeleaf 提供了 Spring 标准方言以及一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
- 多方言支持:它提供了 Thymeleaf 标准和 Spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL 表达式;必要时,开发人员也可以扩展和创建自定义的方言。
- 与 SpringBoot 完美整合:SpringBoot 为 Thymeleaf 提供了的默认配置,并且还为 Thymeleaf 设置了视图解析器,因此 Thymeleaf 可以与 Spring Boot 完美整合。
3.2 Thymeleaf 语法规则
Thymeleaf 作为一种模板引擎,它拥有自己的语法规则。Thymeleaf 的语法分为两类:标准表达式语法和 th 属性
3.2.1 标准表达式语法
Thymeleaf 模板引擎支持多种表达式,以下重点介绍变量表达式
变量表达式:${表达式}
选择表达式:*{表达式}
选择变量表达式与变量表达式功能基本一致,只是在变量表达式的基础上增加了与 th:object 的配合使用。当使用 th:object 存储一个对象后,我们可以在其后代中使用选择变量表达式获取该对象中的属性,其中 * 即代表该对象。
链接表达式:@{表达式}
不管是静态资源的引用,还是 form 表单的请求,凡是链接都可以用链接表达式
消息表达式:#{表达式}
消息表达式一般用于国际化的场景
片段表达式:~{表达式}
片段引用表达式用于在模板页面中引用其他的模板片段
3.2.2 th 属性
Thymeleaf 提供了大量的 th 属性,这些属性可以直接在 HTML 标签中使用,其中常用 th 属性及其示例如下表。
属性 | 功能 | 示例 |
---|
th:text | 文本替换(能够转移特殊字符) | | th:[HTML 标签属性] | 设置任意的 HTML 标签的属性的值 | | th:if | 根据条件判断是否需要展示此标签 | | th:switch | 与 th:case 配合使用,根据不同的条件展示不同的内容 | | th:each | 循环访问元素,支持 Iterable、Map、数组等 | | th:action | 替换表单的提交地址 | | th:src | 替换 HTML 中的 src 属性 | | th:id | 替换 HTML 中的 id 属性 | | th:value | 替换 HTML 中的 value 属性 | | th:style | 设置标签样式 | |
3.3 Thymeleaf 使用流程
接下来通过写一个猜数字的程序来演示 Thymeleaf 模板引擎的使用流程
-
创建一个 Maven 项目,引入 Thymeleaf 依赖(在 maven 中央仓库搜索 Thymeleaf,选择合适版本)
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.15.RELEASE</version>
</dependency>
-
编写 HTML 模板文件(在 webapp/WEB-INF 目录下再创建一个 template 目录用于存放 HTML 模板文件) <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>猜数字游戏</title>
</head>
<body>
<div>
<from action="guessNum" method="post">
<span>请输入要猜的数字:</span>
<input type="text" name="toGuess">
<input type="submit" value="猜">
</from>
</div>
<div>
结果:<span th:text="${result}"></span>
</div>
</body>
</html>
<span th:text="${result}"></span> 将 result 对象替换成该 span 标签的文本内容- result 这个值是从后台渲染而来,如果没网络(直接打开 html 文件)的时候静态数据为空,因为此时 span 标签中没有内容,你要是写了,那就由对应的内容。而如果通过网络访问那么内容将是程序员在 Servlet 中通过计算生成的一个变量
-
编写 Servlet 代码
- 初始化模板引擎对象
- 创建解析器,并设置模板的参数,并将这个模板引擎对象关联这个解析器
- 创建模板的上下文对象,用于对模板中要动态修改的数据进行设置
- 执行模板引擎
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.WebConnection;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@WebServlet("/guessNum")
public class GuessNumServlet extends HttpServlet {
private Random random = new Random();
private int num = 0;
private TemplateEngine engine = new TemplateEngine();
@Override
public void init() throws ServletException {
创建一个 ServletContextTemplateResolver 对象,resolver 表示解析器,这个对象是的功能就是从磁盘上加载 html 模板文件
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(getServletContext());
resolver.setPrefix("/WEB-INF/template/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("utf-8");
engine.setTemplateResolver(resolver);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
resp.setContentType("text/html;charset=utf8");
num = random.nextInt(100)+1;
WebContext webContext = new WebContext(req, resp, getServletContext());
engine.process("guessNum",webContext, resp.getWriter());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf8");
int toGuess = Integer.parseInt(req.getParameter("toGuess"));
String result = "";
if(toGuess > num){
result += "猜大了!";
} else if(toGuess < num){
result += "猜小了!";
} else{
result += "猜对了!";
}
WebContext webContext = new WebContext(req, resp, getServletContext());
webContext.setVariable("result", result);
engine.process("guessNum",webContext, resp.getWriter());
}
}
TemplateEngine 对象表示模板引擎,用于进行页面的渲染ServletContextTemplateResolver 对象表示“解析器”,用于加载 HTML 模板getServletContext() 方法可以获取到这个 webapp 的上下文对象 ServletContext (每个 webapp 都有一个 ServletContext ,并且一个 webapp 中可以有多个 Servlet,这多个 Servlet 中是可以共享 ServletContext 对象的,可以基于这个上下文对象来传递数据)WebContext 对象表示模板上下文,本质是一个哈希表,里面有三个固定参数 req 、resp 、getServletContext() 。通过这个对象的 setVariable() 方法就可以动态替换模板中的可修改数据,该方法的第一个参数是模板中的要动态替换的变量,第二个参数是程序员自己计算生成的变量TemplateEngine 类的 engine() 方法就可以启动模板引擎。该方法的第一个参数是模板文件名,第二个参数是模板上下文对象,第三个参数是 resp.getWriter()
-
代码效果展示
|