页面渲染,所谓渲染就是将数据和页面相结合,根据用户传送的数据不同,页面渲染后的内容也不同。页面渲染可以在服务器端完成,也可以在客户端完成。服务器渲染是将用户传来的数据在服务器端拼接成HTML后传给客户端,而客户端渲染是在客户端将服务器传来的数据拼接成HTML。

页面渲染

服务器渲染

这篇博客主要介绍一下服务器渲染。

我们可以用字符串拼接的方式,将数据拼接成HTML页面。下面我们用字符串拼接的方式完成一个猜数字web版的小游戏。

首先约定好交互方式。

  • GET /guess

通过get请求,从服务器得到一个页面响应,同时在服务器生成一个1-100的随机数。

  • POST /guess

通过post请求,将用户输入的数提交到服务器,在服务器比较用户输入的数和随机数的大小,将比较结果和次数返回。

约定好交互方式后,我们创建一个GuessNumServlet类,关联到/guess路径,用doGet方法实现第一个交互接口,doPost方法实现第二个交互接口。代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@WebServlet("/guess")
public class GuessNumServlet extends HttpServlet {
public int num;
public int count;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//生成一个随机数
Random random = new Random();
//随机数范围在[1,100]
num = random.nextInt(100)+1; //得到一个1-100之间的随机数
//设置响应的类型格式
resp.setContentType("text/html;charset=utf-8");
//字符串拼接响应页面
StringBuilder html = new StringBuilder();
html.append("<form action=\"guess\" method=\"post\">\n" +
" <input type=\"text\" name=\"guessNum\">\n" +
" <input type=\"submit\" value=\"确认输入\">\n" +
" </form>");
//返回响应
resp.getWriter().write(html.toString());
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置响应格式
resp.setContentType("text/html;charset=utf-8");
//得到用户猜的数字
int guessNum = Integer.parseInt(req.getParameter("guessNum"));
String result ="";
//比较用户输入的数字和生成的随机数
if(guessNum<num){
result="猜小了";
}else if(guessNum>num){
result="猜大了";
}else {
result="猜对了";
}
count++;
//字符串拼接响应界面
StringBuilder html = new StringBuilder();
html.append("<form action=\"guess\" method=\"post\">\n" +
" <input type=\"text\" name=\"guessNum\">\n" +
" <input type=\"submit\" value=\"确认输入\">\n" +
" </form>");
html.append("<div>"+result+"</div>");
html.append("<div>"+"猜的次数:"+count+"</div>");
//返回响应界面
resp.getWriter().write(html.toString());
}
}

通过上段代码,我们发现十分简单的页面拼接起来却如此复杂,代码可读性不高。其实我们有一种更好的方式来完成服务器渲染,那就是模板引擎

模板引擎

上面的代码Java和HTML代码都混在了一起,模板引擎可以将JAVA和HTML代码分离,将HTML放到单独的文件。HTML中需要变动的部分用占位符占位,当服务器计算响应完毕后,将HTML模板中的占位符替换成计算后的内容,返回给客户端。

Java中有很多模板引擎,这里用的是Thymeleaf 。

如何使用Thymeleaf,我们上面的猜数字小游戏为例,将上段代码改成模板引擎版本。

首先需要引入依赖。

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>

接着创建一个HTML模板,创建路径是webapps/WEB-INF/templates ,

1
2
3
4
5
6
7
8
<form action="GuessNum" method="post">
<input type="text" name="guessNum">
<input type="submit" value="确认输入">
</form>
<div th:if="${!first}">
<div th:text="${result}"></div>
<div th:text="${count}"></div>
</div>

有$符号的地方就是一个占位符,最终会被替换成计算好的结果。

最后创建Servlet类GuessNumTemplateEngine

  • 创建一个TemplateEngine的类,这个类是服务器渲染的核心类。
  • 初始化模板引擎:创建一个ServletContextTemplateResolver类的解析器,结合ServletContext使用,用来加载要渲染的文件,然后与TemplateEngine相关联。
  • 通过WebContext类将模板文件的变量与Java中的变量相关联。
  • 通过TemplateEngine的process方法完成渲染。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@WebServlet("/GuessNum")
public class GuessNumTemplateEngine extends HttpServlet {
//核心,用于完成页面的渲染
public TemplateEngine engine = new TemplateEngine();
public int randomNum;
public int count;

public void init() {
//对模板引擎初始化
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(this.getServletContext());
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("utf-8");
engine.setTemplateResolver(resolver);
System.out.println("初始化完成");
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Random random = new Random();
randomNum = random.nextInt(100)+1;
WebContext webContext = new WebContext(req,resp,getServletContext());
webContext.setVariable("first",true);
resp.setContentType("text/html;charset=utf-8");
engine.process("guessNum",webContext,resp.getWriter());
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf-8");
//1.获取用户输入的数字
int num = Integer.parseInt(req.getParameter("guessNum"));
String result = "";
// 2. 和 随机数 进行比较
if (num < randomNum) {
result = "猜低了";
} else if (num > randomNum) {
result = "猜高了";
} else {
result = "猜对了";
}
// 3. 自增猜的次数
count++;
//4.返回响应界面
WebContext context = new WebContext(req,resp,getServletContext());
context.setVariable("first",false);
context.setVariable("result",result);
context.setVariable("count",count);
engine.process("guessNum",context,resp.getWriter());
}
}