【AJax】
1.传统开发模式的不足
传统开发模式基于浏览器数据传输功能,页面填写数据/展示数据。浏览器通过访问一个URL地址,将页面的数据提交给服务器。服务器将需要展示的数据返回给浏览器,浏览器再进行数据解析,将数据呈现在用户面前。这种模式主要依赖于浏览器的渲染功能,并且浏览器每次渲染是都是整个页面进行渲染。整个页面包含:样式文件,图片资源,DOM标签.每次浏览器渲染时都要进行重新统一渲染,重新请求一些重复的资源数据.但是实际上变化的只是页面上的数据,一些静态资源没有发生变化.这种统一的重新渲染,导致以下不足:
-
操作服务器额外的负担,因为浏览器重新请求重复数据,服务器又不记录是否发送过,导致服务器重新发送,网络/磁盘读写都造成额外的负担.
-
浏览器重复解析数据,浏览器本身也产生了额外的开销.
程序的设计者,提出了一个理念,能不能只返回想要的数据?如果做到了根据需要返回数据,减少了服务器和浏览器的负担.提出了异步交互的理念.浏览器本身在渲染时,浏览器是占用状态,无法做其它事情的.异步交互,就是指浏览器在渲染时,将渲染的等待时间利用起来,做其它行为.就像同时在做多件事情.
1.1 什么是同步交互
首先用户向HTTP服务器提交一个处理请求。接着服务器端接收到请求后,按照预先编写好的程序中的业务逻辑进行处理,比如和数据库服务器进行数据信息交换。最后,服务器对请求进行响应,将结果返回给客户端,返回一个HTML在浏览器中显示,通常会有CSS样式丰富页面的显示效果。
如果浏览器在使用中,用户都是等待状态.用户全程的参与了整个请求到数据渲染的过程.类似早期排队充值话费/打饭.
1.2 同步交互的不足
-
同步交互的不足之处,会给用户一种不连贯的体验,当服务器处理请求时,用户只能等待状态,页面中的显示内容只能是空白。
-
因为已经跳转到新的页面,原本在页面上的信息无法保存,好多信息需要重新填写
-
这种交互的方式对于服务器和浏览器而言都存在压力.存在性能的损耗
2.异步交互的概念
指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。例如:在支付宝上充值话费.
在部分情况下,我们的项目开发中都会优先选择不需要等待的异步交互方式。将用户请求放入消息队列,并反馈给用户,系统迁移程序已经启动,你可以关闭浏览器了。然后程序再慢慢地去写入数据库去。这就是异步。异步不用等所有操作等做完,就响应用户请求。即先响应用户请求,然后慢慢去写数据库,用户体验较好.类似于多个线程在进行运行.
3.什么是AJax
Ajax是基于异步交互思想,诞生的复合的前端技术.其核心浏览器厂商约定的一套用于进行网络请求数据交互的API.浏览器厂商通过Javascript暴露了一套API,可以用于使用JS时就能通过网络从服务器获取特定的数据,然后在利用DOM技术和CSS技术,实现页面的数据变化.
由于AJax技术是浏览器厂商提供的API,浏览器厂商各自早期没有统一规范,还由于浏览器技术一直在迭代.前端技术一直在更新.市面上就出现了一些对原生ajax技术进行封装的插件.比较早期就是jQuery插件,现在比较流行的axios插件.由于现在前端推荐DOM操作,比较推崇MVVM思想,而jQuery中很大比重的都是在进行DOM操作,很多企业中,提出了去”j”的理念.
3.1 jQuery中的ajax
由于原生的Ajax存在一些不足,浏览器的兼容性,整个请求需要分为5个步骤相对繁琐.基于这样的原因.jQuery插件对原生ajax进行了封装.简化了ajax的使用.
在使用ajax时,开发者主要请求地址和请求参数及返回的数据.jQuery中的ajax在使用时,主要只需要定义请求地址,参数及返回数据的处理即可.
3.1.1 jQuery中ajax使用
-
在页面引入jQuery的JS
-
编写前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jQuery-3.6.0.js"></script>
<style>
p{
color: red;
}
</style>
</head>
<body>
<p id="msg"></p>
<button type="button" id="ajaxBtn">jQuery ajax简单案例</button>
<script>
/* jQuery ajax 简单案例 */
$("#ajaxBtn").click(function () {
// 请求的url
let url = "ajax.do";
// 请求参数
let param = {name:"韩梅梅",age:18};
$.get(url,param,function (rs) {
console.log("返回数据为:",rs);
$("#msg").html(rs);
})
});
</script>
</body>
</html>
3.1.2 jQuery中核心方法
3.1.3 ajax方法核心配置参数
3.1.3.1 ajax方法的演示
3.1.3.1.1 前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/jQuery-3.6.0.js"></script>
<style>
p{
color: red;
}
</style>
</head>
<body>
<p id="msg"></p>
<button type="button" id="ajaxBtn">jQuery ajax简单案例</button>
<button type="button" id="ajax">ajax方法演示</button>
<script>
/* jQuery ajax 简单案例 */
$("#ajaxBtn").click(function () {
// 请求的url
let url = "ajax.do";
// 请求参数
let param = {name:"韩梅梅",age:18};
$.get(url,param,function (rs) {
console.log("返回数据为:",rs);
$("#msg").html(rs);
})
});
/* 演示ajax 方法 */
$("#ajax").click(function () {
let m = 10;
let settings = {
url:"jquery.do",// 请求地址
type:"post",// 请求方法
timeout:1000,// 超时时间 1秒
data:{ // 请求参数
name:"韩梅梅",
age:18
},
async:false,// 是否异步 如果是异步 则 ajax 函数没有执行完成就能执行之后的程序.如果非异步则必须等待ajax程序执行完成才能执行之后的程序
// 如果ajax函数中的数据要参与之后程序的运算,必须设置非异步 false
dataType:"json", // 期望返回的数据类型,一般浏览器会将返回的数据当做自己期望的类型,如果不是期望类型则程序会异常,通过火狐浏览器查看
beforeSend:function () {
console.log("我要请求了!!!!")
//
console.log("加载中....在转圈圈...")
},
success:function (data,req,xh) { // 请求成功时调用的方法
console.log(data) // 返回的数据
console.log(req) // 消息
console.log(xh) // XMLHttpRequest 对象
// 将数据放入到 p 标签
$("#msg").html(data);
// 改变 m的值
m = m + 100;
},
complete:function () {
// 标识请求完成调用的函数 不论成功还是失败都会调用
console.log("取消转圈圈...")
},
error:function (xh,status) { // error 两种情况触发 :1. url地址错误 2.服务器内部程序异常
console.log("xh:",xh) // ajax 对象
console.log("status:",status) // 错误信息
}
};
$.ajax(settings);
console.log("m的值:",m);
});
</script>
</body>
</html>
3.1.3.1.2 后端代码
package com.powernode;
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 java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/jquery.do")
public class JQueryAjaxServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/* try {
// 线程休眠 5 秒
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}*/
String name = req.getParameter("name");
String age = req.getParameter("age");
System.out.println(name +" " +age);
PrintWriter writer = resp.getWriter();
/* html head body p style */
writer.print("{\"name\":\"Hello jQuery ajax\"}");
writer.flush();
writer.close();
}
}
3.1.4 get/post方法演示
在实际使用中,开发者只关注2个点,请求信息和返回的数据.jQuery提供一些方法对ajax方法进行简化,如:get(url,[param],function)/post(url,[param],function).分别表示get类型的异步请求和post异步请求(比较常用).
3.1.4.1 演示案例
/**
* 演示 get 方法
*/
$("#get").click(function () {
$.get("jquery.do",{name:"韩梅梅",age:18},function (rs) {
console.log(rs)
$("#msg").html(rs);
})
});
/**
* 演示 post 方法
*/
$("#post").click(function () {
$.post("jquery.do",{name:"韩梅梅",age:18},function (rs) {
console.log(rs)
$("#msg").html(rs);
})
});
3.2 axios的使用
首先要在页面引入axios的js插件.参考:axios中文网.
3.2.1 axios应用
3.2.1.1 后端代码
package com.powernode;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/axios.do")
public class AxiosServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
// ServletInputStream inputStream = req.getInputStream();
// byte[] b = new byte[1024];
// int len = inputStream.read(b);
// System.out.println(new String(b,0,len,"UTF-8"));
String name = req.getParameter("name");
String age = req.getParameter("age");
// 将字符串转 对象
System.out.println(name +" " +age);
PrintWriter writer = resp.getWriter();
/* html head body p style */
writer.print("{\"name\":\"Hello axios ajax\"}");
writer.flush();
writer.close();
}
}
3.2.1.2 前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/axios.js"></script>
</head>
<body>
<p id="msg"></p>
<button id="get">不带参数的get请求</button>
<button id="getParam">带参数的get请求</button>
<button id="post">post请求</button>
<script>
/**
* 演示 axios 的get 请求
* axios 默认就是 get 请求
*/
document.getElementById("get").onclick = function () {
axios.get("axios.do")
// 请求完成时调用的函数
.then(function (response) {
// 整个响应数据对象
console.log(response);
// 默认的axios的配置信息
console.log(response.config);
// service 方法返回的具体的数据
console.log(response.data);
// 返回的响应头信息
console.log(response.headers);
// http 响应码
console.log(response.status);
}) // 请求发生异常时调用的函数
.catch(function (error) {
console.log(error);
});
}
/**
* 带参数的get 请求
*/
document.getElementById("getParam").onclick = function () {
axios.get("axios.do",{
params:{
name:"韩梅梅",
age:18
}
})
// 请求完成时调用的函数
.then(function (response) {
// 整个响应数据对象
console.log(response);
// 默认的axios的配置信息
console.log(response.config);
// service 方法返回的具体的数据
console.log(response.data);
// 返回的响应头信息
console.log(response.headers);
// http 响应码
console.log(response.status);
}) // 请求发生异常时调用的函数
.catch(function (error) {
console.log(error);
});
}
/**
* post 请求
*/
document.getElementById("post").onclick = function () {
axios.post("axios.do",{
name:"韩梅梅",
age:18
},{
// 为 post 请求 兼容表单 URL参数编码问题
transformRequest: [function (data, headers) {
console.log(" 数据格式处理.....")
console.log(data)
// 对 data 进行任意转换处理
// {name:"hanmeimei",age:18} ---> name=韩梅梅&age=18
let formData = new Array();
// 循环对象
for(name in data){
// item 对象中的属性 : name age
// 根据动态的属性名 获取对应的值
let value = data[name];
console.log(name,"=",value)
formData.push(name+"="+value)
}
console.log(formData)
formData = formData.join("&");
console.log(formData)
return formData;
}],
}
)
// 请求完成时调用的函数
.then(function (response) {
// 整个响应数据对象
console.log(response);
// 默认的axios的配置信息
// console.log(response.config);
// service 方法返回的具体的数据
//console.log(response.data);
// 返回的响应头信息
//console.log(response.headers);
// http 响应码
//console.log(response.status);
}) // 请求发生异常时调用的函数
.catch(function (error) {
console.log(error);
});
}
</script>
</body>
</html>
4.浏览器跨域访问
在Ajax请求中,JS是基于浏览器进行网络通信的.这种功能必须依附浏览器,出于安全的考虑,浏览器会对JS通信的数据进行检查.浏览器对数据检查通过之后,才会将通信数据移交给JS程序.浏览器最基本检查策略叫同源策略.是一种最基本安全保护机制
4.1 同源策略
在网络访问中,必须存在3种数据:协议/域名/端口.如果3种数据一致就标识同源访问,如果不一致就是非同源访问.默认浏览器只支持同源访问.
以下就是非同源访问的浏览器异常信息.也被称之为跨域访问.
当前浏览器访问地址:http://localhost:8080/ajax_crud/index.html
ajax的访问地址:http://127.0.0.1:8080/ajax_crud/user.do
由于当前浏览器地址的域名:localhost,但是ajax的地址是127.0.0.1虽然都是标识同一个地址,但是浏览器检测时认为和自己的不一致,所以进行抛出了异常,认为存在跨域访问.
4.2 跨域解决
跨域问题解决方案比较多,例如:jsonp,服务器允许跨域访问设置.浏览器既然会检查数据,服务器返回数据时,直接通知浏览器本次访问是允许跨域访问的.需要通过响应头通知浏览器.
// 允许跨域访问 * 任何访问源
resp.addHeader("Access-Control-Allow-Origin","*");
5.Ajax综合案例
利用ajax 实现增/删除/查/改.
5.1 后端代码
5.1.1 servlet
package com.powernode.servlet;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.powernode.dao.UserDao;
import com.powernode.domain.User;
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 java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
/**
* 在servlet中 默认只有service 能够提供服务
* 那么出现 同类请求存在多个 例如 : 用户的请求 新增 删除 修改 查询 等等 难道创建多个 ?
* 通过设计的方式解决问题:
* 每个操作中,要求额外传递一个参数 标识是什么操作. 例如 : 如果是新增 则 传递 service = add
* 删除 则传递 service = delete
* 修改 则传递 service = update
* 查询 则传递service = query
* 在service 方法中 根据 service 参数的值 进行 分条件调用
*/
@WebServlet("/user.do")
public class UserServlet extends HttpServlet {
UserDao userDao = new UserDao();
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String service = req.getParameter("service");
if (StrUtil.equals(service,"add")){
// 处理 新增的方法
add(req,resp);
}else if (StrUtil.equals(service,"delete")){
// 处理 新增的方法
delete(req,resp);
}else if (StrUtil.equals(service,"query")){
// 处理 新增的方法
query(req,resp);
}else if (StrUtil.equals(service,"update")){
// 处理 新增的方法
update(req,resp);
}
}
/**
* 处理更新请求
* @param req
* @param resp
*/
private void update(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String id = req.getParameter("id");
String realname = req.getParameter("realname");
String username = req.getParameter("username");
String password = req.getParameter("password");
userDao.update(Integer.parseInt(id),username,password,realname);
// 使用UTF-8格式处理字节数据
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
//输出数据
writer.write("success");
writer.flush();
writer.close();
}
/**
* 处理查询请求
* @param req
* @param resp
*/
private void query(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 获取输入的姓名
String realname = req.getParameter("realname");
List<User> users = userDao.selectAll(realname);
// 使用JSON格式输出
// 表示返回json格式数据
resp.setContentType("text/json;charset=utf-8");
// 使用UTF-8格式处理字节数据
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
// 将 List 转 JSON字符串
String data = JSON.toJSONString(users);
//输出数据
writer.write(data);
writer.flush();
writer.close();
}
/**
* 处理删除请求
* @param req
* @param resp
*/
private void delete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String id = req.getParameter("id");
userDao.delete(Integer.parseInt(id));
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
//输出数据
writer.write("success");
writer.flush();
writer.close();
}
/**
* 处理新增请求
* @param req
* @param resp
*/
private void add(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String realname = req.getParameter("realname");
String username = req.getParameter("username");
String password = req.getParameter("password");
userDao.add(username,password,realname);
// 使用UTF-8格式处理字节数据
resp.setCharacterEncoding("UTF-8");
PrintWriter writer = resp.getWriter();
//输出数据
writer.write("success");
writer.flush();
writer.close();
}
}
5.1.2 dao
package com.powernode.dao;
import cn.hutool.core.util.StrUtil;
import com.powernode.domain.User;
import java.util.List;
/**
* 用户表操作类
*/
public class UserDao extends BaseDao {
/**
* 新增用户
* @param username
* @param password
* @param realname
*/
public void add(String username, String password,String realname) {
String sql = "insert into user (username,password,realname) value(?,?,?)";
super.executeUpdate(sql,username,password,realname);
}
/**
* 删除用户
* @param id
*/
public void delete(Integer id) {
String sql = "delete from user where id=?";
super.executeUpdate(sql,id);
}
/**
* 修改用户
* @param id
* @param username
* @param password
* @param realname
*/
public void update(Integer id, String username, String password,String realname) {
String sql = "update user set username = ?,password=? ,realname=? where id = ?";
super.executeUpdate(sql,username,password,realname,id);
}
/**
* 查询所有的用户
* @param realname
* @return
*/
public List<User> selectAll(String realname) {
// TODO 多个条件 该如何拼接 动态SQL
String sql = "select id,username,password,realname from user";
if (StrUtil.isNotBlank(realname)){
sql = sql +" where realname like '%"+realname+"%' ";
}
return super.executeQueryList(sql,User.class);
}
}
5.2 列表页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户列表</title>
</head>
<body>
<p><input id="realname"/>
<button id="searchBtn">查询</button>
</p>
<hr>
<a href="add.html">新增</a>
<hr>
<table id="dataTable">
<tr>
<td>ID</td>
<td>登录名</td>
<td>密码</td>
<td>姓名</td>
<td>操作</td>
</tr>
</table>
<script src="js/jQuery-3.6.0.js"></script>
<script>
// 全局性的存储 当前table 用户信息
let globalUser ;
/**
* 渲染表格
*/
function renderTable() {
// 获取搜索的关键词
let realname = $("#realname").val();
$.get("user.do", {service: "query", realname: realname}, function (rs) {
// 数组长度如果为 0 表示没有数据
if (rs.length == 0) {
// 结束程序
return false;
}
globalUser = rs;
// 获取表格dom 对象
let table = $("#dataTable");
// 清空表格
// 获取所有 tr 但是 索引大于 0
$("tr:gt(0)").remove();
for (let user of rs) {
let id = user.id;
let username = user.username;
let password = user.password;
let realname = user.realname;
tr ="<tr><td>"+id+"</td>"+"<td>"+username+"</td>"+"<td>"+password+"</td>"+"<td>"+realname+"</td><td><button οnclick='del("+id+")'>删除</button><button οnclick='update("+id+")'>修改</button></td></tr>"
table.append(tr);
}
});
}
$("#searchBtn").click(function () {
renderTable();
});
renderTable();
/**
* 删除方法
* @param id
*/
function del(id) {
$.get("user.do",{service:"delete",id:id},function (rs) {
if (rs == "success"){
renderTable();
return false;
}
alert("删除失败")
})
}
function update(id) {
// 循环所有的用户
for (let user of globalUser) {
// 如果id一直 说明 就是要修改的用户的
if (user.id == id){
// user 本身是 object
// sessionStorage 存储的 字符串类型
// JSON.stringify(user) 将对象转化为 json 字符串
sessionStorage.setItem("user",JSON.stringify(user))
}
}
// 跳转到更新页面
location.href='update.html';
}
</script>
</body>
</html>
**5.3 新增列表**
```c
```c
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form >
<p>姓名:<input name="realname" id="realname" /></p>
<p>用户名:<input name="username" id="username" /></p>
<p>密码:<input name="password" id="password" /></p>
<button type="button" id="subBtn">提交</button>
<button type="button" id="back">返回</button>
</form>
<script src="js/jQuery-3.6.0.js"></script>
<script>
$("#subBtn").click(function () {
let realname = $("#realname").val();
let username = $("#username").val();
let password = $("#password").val();
$.post("user.do",{service:"add",realname:realname,username:username,password:password},function (rs) {
// 如果返回值 success 表示添加成功
// 返回到 index页面
if (rs == 'success'){
location.href = "index.html";
return false;
}
alert("添加失败!");
})
});
$("#back").click(function () {
location.href = "index.html";
});
</script>
</body>
</html>
**5.4 修改页面**
```c
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form >
<p>姓名:<input name="realname" id="realname" /></p>
<p>用户名:<input name="username" id="username" /></p>
<p>密码:<input name="password" id="password" /></p>
<button type="button" id="subBtn">提交</button>
<button type="button" id="back">返回</button>
</form>
<script src="js/jQuery-3.6.0.js"></script>
<script>
let user ;
//向表单填充数据
function initForm() {
user = sessionStorage.getItem("user");
console.log(user)
// 将字符串转对象
user = JSON.parse(user);
$("#realname").val(user.realname);
$("#username").val(user.username);
$("#password").val(user.password);
}
initForm();
$("#subBtn").click(function () {
let realname = $("#realname").val();
let username = $("#username").val();
let password = $("#password").val();
$.post("user.do",{service:"update",id:user.id,realname:realname,username:username,password:password},function (rs) {
// 如果返回值 success 表示添加成功
// 返回到 index页面
if (rs == 'success'){
location.href = "index.html";
return false;
}
alert("修改失败!");
})
});
$("#back").click(function () {
location.href = "index.html";
});
</script>
</body>
</html>