在数据库CRUD这个例子中,可以看到,浏览器端展示的页面,在每次操作之后都是完全由服务器返回的页面,即页面是全部刷新的,称为全局刷新 。但是有时候,我们进行一步操作之后,页面展示的内容并没有完全发生变化,即只有一小部分页面的内容发生了变化,那么这个时候如果还是采用全局刷新的形式,就会有很多无用的数据传输,响应时间变长,即浪费了带宽资源,也影响了用户体验。所以可以采用局部刷新 (AJAX, Asynchronous JavaScript and XML),只传输需要改变的内容数据。
全局刷新的流程如下所示:
异步刷新的流程如下所示:
1. 异步请求对象 在全局刷新中,浏览器向服务器发起请求,服务器响应内容,浏览器根据响应内容渲染页面。那么在局部刷新中,由异步对象(XMLHttpRequest) 代替浏览器发起请求,并获取数据(服务端只会原路返回响应数据,所以谁发送的数据,谁就会收到数据),将其展示在浏览器页面上。在浏览器内存中,可以创建多个异步对象,每个都可独立地发起请求。
XMLHttpRequest对象能够:
在不重新加载页面的情况下更新网页;
在页面已加载后向服务器请求数据;
在页面已加载后从服务器接收数据。
AJAX, 即Asynchronous JavaScript and XML,和同步相对应,异步的JavaScript和xml;AJAX并不是一种语言,是一种方法。本质上AJAX属于JavaScript的内容,异步刷新对象可由JS来创建,如下所示:
1 var xmlhttp = new XMLHttpRequest();
JavaScript负责创建异步对象,发送请求,更新页面的dom对象。XML用于网络中传输数据的格式(现在一般用json格式来传输数据)。
异步刷新的步骤如下所示:
创建异步对象;
1 var xmlhttp = new XMLHttpRequest();
给异步对象绑定事件(onreadystatechange )
当异步对象发起请求或者获取数据,都会触发这个事件(实际上异步对象有一个属性,该属性readyState 会随着请求对象的状态变化而变化,该属性的变化对应这个事件)。我们可以指定一个函数来处理相关操作(如刷新页面)。
1 2 3 xmlHttp.onreadystatechange = function ( ) { 处理请求的状态变化。 }
readyState 属性代表异步对象XMLHttpRequest的状态,共5个(0,4):
0,请求未初始化,即刚刚创建完异步请求对象,var xmlhttp = new XMLHttpRequest();
;
1,初始化异步请求对象,xmlhttp.open(请求方法,请求地址,true)
;
2,异步对象发送请求,xmlhttp.send()
;
3,异步对象接收应答数据,即从服务端返回数据,XMLHttpRequest内部处理。
4,异步请求对象已经将数据解析完毕,此时才可以读取数据。开发人员根据数据来更新当前页面。所以我们主要判断异步对象的该属性是否为此状态。
除此之外,因为是网络请求,所以还需要异步请求对象的另一个属性status ,表示网络响应的状态码。常见的状态码如下所示:
200,请求成功
404,资源未找到
405,服务器不处理请求中的方法(如post、get等等)
500,服务器内部错误
所以,当status为200,并且readyState为4时,表示异步对象成功获得了请求成功并响应的数据,程序员可以进一步操作 。
初始化异步请求对象的参数
根据异步对象的方法open()
初始化异步请求对象的参数,即xmlHttp.open(请求方法get|post,请求地址,异步true(默认)|同步请求false)
。
异步对象发送请求
根据异步对象的方法xmlHttp.send()
方法发送请求。发送请求后,等待服务器端响应。那么怎么获取服务器端的数据呢?
利用上述第2步的readyState和status两个属性判断,判断成功后,利用异步请求对象的responseText
属性获取数据。
2. 异步刷新案例 简单的一个Servlet案例,计算BMI值,由前端提交数据,后端计算好BMI后返回给前端。
2.1 首先前端采用jsp来做,异步刷新采用4步骤。 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 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>局部刷新</title> <script type="text/javascript" > function doAjax () { var xmlHttp = new XMLHttpRequest(); xmlHttp.onreadystatechange = function(){ alert("readyState:" + xmlHttp.readyState); }; xmlHttp.open("post" , "/Ajax01/asyn" , true ); xmlHttp.send(); } </script> </head> <body> <!-- 注意,Ajax不局限于form表单提交,采用异步对象发送请求--> 姓名:<input type="text" name="name" id="name" ><br> 身高(m):<input type="text" name="height" id="height" ><br> 体重(kg):<input type="text" name="weight" id="weight" ><br> <input type="button" value="计算" onclick="doAjax()" > </body> </html>
可以先在第2步测试一下异步对象的状态变化。
首先是初始化异步对象(状态为1,状态0是取不到的)。
点击确定按钮后,然后是异步对象发送请求(状态为2)。注意,下面的type为xhr指的就是XML Http Request
的简称。可以看到发生了网络数据的传输
之后,点击确定按钮,状态为4。(因为服务器端没有返回数据,所以这里没有状态3)
服务端返回数据,利用status和readyState来获取数据并显示出来 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <script type="text/javascript" > function doAjax () { var xmlHttp = new XMLHttpRequest(); xmlHttp.onreadystatechange = function(){ if (xmlHttp.status == 200 && xmlHttp.readyState == 4 ){ alert("数据:" + xmlHttp.responseText); } }; xmlHttp.open("post" , "/Ajax01/asyn" , true ); xmlHttp.send(); } </script>
如下所示,可以看到即使收到服务器返回的数据,浏览器端的页面并没有变化。
可以看到,xmlHttp.responseText实际上就是Servlet中输出流输出的内容。 经过上述测试,完整的jsp页面如下所示:
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 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>局部刷新</title> <script type="text/javascript" > function doAjax () { var xmlHttp = new XMLHttpRequest(); xmlHttp.onreadystatechange = function(){ if (xmlHttp.status == 200 && xmlHttp.readyState == 4 ){ document.getElementById("data" ).innerText = xmlHttp.responseText; } }; var height = document.getElementById("height" ).value; var weight = document.getElementById("weight" ).value; var param = "height=" + height + "&weight=" + weight; xmlHttp.open("post" , "/Ajax01/asyn" , true ); xmlHttp.setRequestHeader("Content-type" , "application/x-www-form-urlencoded" ); xmlHttp.send(param); } </script> </head> <body> <!-- 注意,Ajax不局限于form表单提交,采用异步对象发送请求--> 姓名:<input type="text" name="name" id="name" ><br> 身高(m):<input type="text" name="height" id="height" ><br> 体重(kg):<input type="text" name="weight" id="weight" ><br> <input type="button" value="计算" onclick="doAjax()" ><br> <br> <div id="data" >等待服务器计算并返回数据...</div> </body> </html>
2.2 服务器采用Servlet处理请求 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class AsynServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8" ); String height = request.getParameter("height" ); String weight = request.getParameter("weight" ); double bmi = Double.parseDouble(weight) / Math.pow(Double.parseDouble(height), 2 ); System.out.println(bmi); response.setContentType("text/html; charset=UTF-8" ); PrintWriter out = response.getWriter(); out.write(String.valueOf(bmi)); } }
备注,其实send方法的参数就是请求体中的内容,如果采用get请求方式,则不需要参数;如果采用post请求方式,则需要设置传递的参数。
2.3 总结 本质上利用异步请求对象,然后根据请求方式的不同,构建URL即可。服务器端Servlet没什么区别。利用异步对象的readyState和status两个属性来判断是否正常请求和响应,然后利用异步对象的responseText属性来获取数据,进而根据id来局部更新页面的内容。
异步对象为什么称为异步呢?当其发起请求后,不用等待数据处理完毕,就可以执行其他的操作,等上面的处理完毕后再回来继续执行操作。相当于多线程,不同的线程之间互不影响。
3. json 上面的例子中,服务端仅仅是返回的简单数据,如果返回的是比较复杂的数据,比如性别、年龄、身高、体重等等这些很多个字段的数据该怎么办呢?必然是将其封装成对象 ,但是这些数据前端需要拿到,所以封装的对象,前后端要都有这种类型对象 ,可以采用json来做(但是网络传输,在另一端一定是字符串形式,即json形式的数据转换成的字符串,在另一端还要将字符串转换成json对象)。除此之外,json还有以下特点:
json格式好理解;
json格式数据在多种语言中,比较容易处理,使用Java、JavaScript读写json格式的数据比较容易;
json格式数据在网络中传输较快。
常用的json工具包有Gson、FastJson、Jackson、Json-lib。这些库有的依赖较多、性能差,有的性能较好,有的规范不是很全面。介绍一个常用的Jackson工具库,Json工具库和JDBC一样,属于标准,需要第三方实现,这里采用Jackson工具包,将所需要的三个jar包导入到工程lib中即可。
**注意,如果服务器端响应的数据为json格式数据的话,需要设置响应内容类型response.setContentType("application/json;charset=utf-8")
**。
3.1 案例 服务器端返回json格式的数据(字符串),浏览器接收到后(字符串形式),将其转换成json格式数据读取并显示。首先简单测试一下Json工具库,该库中有一个ObjectMapper
类,该类有一个writeValueAsString
方法,将对象映射为Json格式的字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class JsonTest01 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { User u = new User("张三" , 23 , 1.75 , 68 ); System.out.println("采用toString方法输出对象:" ); System.out.println(u); ObjectMapper om = new ObjectMapper(); String json = om.writeValueAsString(u); System.out.println("采用映射类将对象转换成json类型字符串:" ); System.out.println(json); } }
然后,服务端创建json格式类型的字符串,将其响应到浏览器端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class JsonTest01 extends HttpServlet { @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { User u = new User("张三" , 23 , 1.75 , 68 ); ObjectMapper om = new ObjectMapper(); String json = om.writeValueAsString(u); response.setContentType("application/json;charset=utf-8" ); PrintWriter out = response.getWriter(); out.print(json); } }
简单的前端页面如下所示(注意,如何将收到的json字符串转换为json对象 ):
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 52 53 54 55 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>测试Json</title> </head> <script type="text/javascript" > function doSubmit () { var xmlHttp = new XMLHttpRequest(); xmlHttp.onreadystatechange = function () { if (xmlHttp.readyState == 4 && xmlHttp.status == 200 ){ var responseText = xmlHttp.responseText; var json = eval("(" + responseText + ")" ); document.getElementById("name" ).value = json.name; document.getElementById("age" ).value = json.age; document.getElementById("height" ).value = json.height; document.getElementById("weight" ).value = json.weight; } }; xmlHttp.open("post" , "/Ajax02/json" , true ); xmlHttp.send(); } </script> <body> <form action="/Ajax02/json" method="post" > <input type="button" value="提交" onclick="doSubmit()" ><br> <table border="1px" > <tr> <td>姓名</td> <td>年龄</td> <td>身高</td> <td>体重</td> </tr> <tr> <td><input type="text" id="name" readonly></td> <td><input type="text" id="age" readonly></td> <td><input type="text" id="height" readonly></td> <td><input type="text" id="weight" readonly></td> </tr> </table> </form> </body> </html>
可以看到浏览器中的响应结果以及网络流json如下所示:
4. 总结 AJAX异步刷新本质上属于JS上的内容,通过创建异步对象(区别于浏览器自己发送请求,相当于多线程,不影响浏览器其他组件的运行),独立发送请求并接受数据,然后设置浏览器其他组件的value,从而达到异步刷新。有以下四个步骤:
创建异步对象
绑定事件(异步刷新页面部分内容)
初始化异步对象
异步对象发送请求
另外,传送的数据有时候比较多,比较复杂,服务器端可以将其转换为Json格式的字符串,然后浏览器端收到数据后,将其转换为json对象,再取值。
5. 备注 参考B站《动力节点》。