Ajax笔记
1. 概述(了解)
1.1同步与异步概念
相关知识复习 (对于今天来说,不重要):
阻塞:在能开始做某件事件之前一直等着什么都不做,直到轮到你了就开始做要做的这件事情。
同步:自己亲自出马做这个事情
异步:让小弟帮你去做事情,做完了给反馈。
同步不一定阻塞。
- 同步阻塞(
BIO
)。排队等吃饭,在轮到我之前,我什么都不做,一直盯着门口直到有位子可以坐下来吃饭。 - 同步非阻塞(
NIO
)。排队等吃饭,不想干等着,就去逛街,但是每个1分钟回来看下有没有轮到自己。(轮询) - 只要是同步,必须是自己亲自做 -- 亲自排队。
异步肯定非阻塞。
- 异步非阻塞(
AIO
):排队吃饭,在微信公众号上叫个号(相当于留了个微信),然后去逛街,到号了微信上直接通知你有位子了去吃饭。 - 异步不需要自己亲自做,不用自己亲自排队,安排了微信公众号这个小弟帮你排队。
B/S结构中的同步和异步:
同步请求:浏览器在解析到网页代码某一行时,该行发起了一个请求;在服务器响应该请求前,浏览器什么都不能做,必须得等到请求完成返回数据之后,才会执行后续的代码;这期间浏览器处于假死状态
异步请求:浏览器在解析到网页代码某一行时发起请求后,浏览器可以继续执行后面的代码,继续做其他任何事情,而不用等待服务器响应。会有回调函数接收服务器响应数据并解析渲染。异步请求并不会影响页面的加载与用户的操作,相当于是在两条线上,各走各的,互不影响。
AJAX中的异步请求和局部刷新
AJAX:Asynchronous JavaScript and XML,异步的JavaScript
和xml
。现在应该叫(AJAJ
异步请求:用户对客户端的操作和服务器之间是异步非阻塞的。
局部刷新:把服务器响应的数据放在网页的某一区域刷新展示,而不会造成整个页面刷新。
体验好:不需要刷新整个页面,就不会有闪那一下;
效率高:因为可以只更新N条数据中的一条。
AJAX 不是新的编程语言,而是一种使用现有技术的新的用法。
2. 原生JS
实现AJAX(了解)
知道原生JS
操作的核心对象:XMLHTTPRequest
<body>
<form autocomplete="off">
姓名:<input type="text" id="username">
<span id="uSpan"></span>
<br>
密码:<input type="password" id="password">
<br>
<input type="submit" value="注册">
</form>
</body>
<script>
//1.为姓名绑定失去焦点事件
document.getElementById("username").onblur = function() {
//2.创建XMLHttpRequest核心对象,使用原生js完成异步请求的核心对象
let xmlHttp = new XMLHttpRequest();
let username = document.getElementById("username").value;
//3.打开链接
// 第一个参数是请求方式 get/post
// 第二个参数是 请求的url(get方式包含参数)
// 第三个参数true表示异步,false表示同步
xmlHttp.open("GET","userServlet?username="+username,true);
//xmlHttp.open("GET","userServlet?username="+username,false);
//5.设置监听,处理响应。原理上是应该先监听,后发送请求,这样服务器的响应肯定会被监听监听到并解析
xmlHttp.onreadystatechange = function() {
//判断请求和响应是否成功
if(xmlHttp.readyState == 4 && xmlHttp.status == 200) {
//将响应的数据显示到span标签
document.getElementById("uSpan").innerHTML = xmlHttp.responseText;
}
};
//4. 发送请求
xmlHttp.send();
}
</script>
3. jQuery
实现AJAX
3.1 $.get()
(重点)
<form autocomplete="off">
姓名:<input type="text" id="username">
<span id="uSpan"></span>
<br>
密码:<input type="password" id="password">
<br>
<input type="submit" value="注册">
</form>
</body>
<script src="js/jquery-3.3.1.min.js"></script>
<script>
//1.为用户名绑定失去焦点事件
$("#username").blur(function () {
let username = $("#username").val();
//2.jQuery的GET方式实现AJAX
/*
第一个参数 请求路径
第二个参数 请求的参数
支持json {key:value}
支持键值对 key=value
第三个参数 回调函数
用来处理服务器响应的数据的函数
形参data 接收服务器响应的数据
第四个参数 type 浏览器以何种形式解析服务器的响应数据
text/不设置 浏览器以普通文本字符串的形式解析使用响应数据
json 浏览器以json的形式解析使用响应数据,解析成js对象
xml 浏览器以xml的形式解析使用响应数据,解析成document对象
*/
// $.ajaxSettings.Xxxx
$.get(
//请求的资源路径
"userServlet",
//请求参数
"username=" + username,
//回调函数
function (xxxx) {
//将响应的数据显示到span标签
$("#uSpan").html(xxxx);
},
//响应数据形式
"text"
);
});
</script>
3.2 $.post()
(重点)
<body>
<form autocomplete="off">
姓名:<input type="text" id="username">
<span id="uSpan"></span>
<br>
密码:<input type="password" id="password">
<br>
<input type="submit" value="注册">
</form>
</body>
<script src="js/jquery-3.3.1.min.js"></script>
<script>
//1.为用户名绑定失去焦点事件
$("#username").blur(function () {
let username = $("#username").val();
// let username = document.getElementById("username").value;
// 2.jQuery的POST方式实现AJAX
/*
和get方式请求的区别:
1. 使用方法是$.post
2. 请求资源路径后不能跟请求参数,请求参数只能放在post方法第二个参数位置
其他没有任何区别
*/
/*
如果想在post请求或者get请求的时候指定更多参数
可以在$.post/$.get发起请求之前,通过$.ajaxSettings.xxx设置更多参数
*/
// $.ajaxSettings.Xxxx
$.post(
//请求的资源路径
"userServlet",
//请求参数
"username=" + username,
//回调函数
function (data) {
//将响应的数据显示到span标签
$("#uSpan").html(data);
},
//响应数据形式
"text"
);
});
</script>
3.3 $.ajax({json串})
(了解)
/*
$.ajax({key:value,...}); // 所有参数通过json格式传递
url:请求的资源路径。
async:是否异步请求,true-是,false-否(默认是true)。
data:发送到服务器的数据,可以是键值对形式,也可以是js对象形式。
type:请求方式,POST或GET(默认是GET)。
dataType:返回数据的类型,取值可以是xml,html,js,json,text等,告诉浏览器以该格式解析。
success:请求成功时调用的回调函数,后跟匿名函数对象。
error:请求失败时调用的回调函数,后跟匿名函数对象。
书写麻烦,不推荐使用。
*/
`$.get()`和`$.post()`同样可以实现。
只需要在调用这两个方法前,通过`$.ajaxSettings.xxx= yyyy`;
4. Json
格式转换
4.1 Json
概念
Json
是一种轻量级的数据交换格式。
轻量级:相对于XML
来说,Json
本身就是一串字符串,数据量小且生成和解析简单。
数据交换格式:客户端和服务器传递数据的格式。
Json
组成规则:
- 由键值对组成,由
{}
包裹; - 键和值都用
""
包裹,键值之间使用:
拼接,多个键值对之间使用,
分割;键的双引号可以省略不写; - key可以为字符串,value可以为数字、字符串、布尔、数组、
json
对象、json
对象数组。
分类:
Json
对象:{"key1":"value1", "key2":"value2"}
Json
数组:[{"key1":"value1","key2":"value2"},{"key3":"value3","key4":"value4"}]
- 复杂
Json
:{"k1":"v1","k2":[{"k21":"v21","k22":"v22"},{"k3":"v3","k4":"v4"}]}
使用:
-
前端中,
Json
中有两种形式- 以
Json
对象的形式存在,我们称之为Json对象
(本质是一个js
对象)。当我们需要使用Json
中数据的时候,可以通过json对象.key
获取对应的值; - 以
Json
格式的字符串形式存在,我们称之为Json字符串
。当我们需要在前端需要和后台交换数据的时候,会把Json
对象转成Json
字符串; - 两种形式可以使用
JSON
类完成转换。stringify
(json
对象)转成str
,parse
(str
)解析json
对象。
前后台交换数据,一般都是通过字符串作为载体。所以说,如果要把某个
json
对象的内容传递到后台,一般先要把它转成json
格式的字符串,然后再发送。 - 以
-
后台中,
Json
是以字符串形式存在-
后台中数据传递的载体是
Model
对象,前后台数据交换格式是json
; -
后台会做对象(model/list/map)和
Json
字符串之间的转换; -
手动转换:获取
java
对象的属性和对应的值,手动按照json
要求的格式拼接成json
字符串;手动切割Json
字符串,然后设置到对象中; -
自动转换:使用第三方的工具类,完成
java
对象和json
字符串的相互转换。
-
4.2 常见Json
转换工具
两个概念
- 序列化:java对象转换到
Json
字符串 - 反序列化:
Json
字符串转换到java对象
常见工具(Json
解析库)及特点
Jackson
: 序列化效率最高,SpringMVC
底层默认使用fastjson
:阿里巴巴公司开源,反序列化效率最高,移动端诸如Android
使用较多gson
:谷歌公司开源,对复杂json
支持最好,稳定;国内使用不多
4.3 JavaBean
对象转Json
字符串(重点)
// 导包,三个
// jackson-annotations-2.2.3.jar
// jackson-core-2.2.3.jar
// jackson-databind-2.2.3.jar
// 核心对象
OjbectMapper om = new ObjectMapper();
// 常用的是序列化,解析请求的时候是反序列化,不存在json格式字符串
// 转换成字符串
String jsonStr = om.writeValueAsString(bean/list/map);
// 转换成字符串并写入输出流,可以配合respone使用(response.getWriter()/getOutputStream())
om.writeValue(response.outputStream()/response.getWriter(),bean/list/map);
4.4 Json
字符串转JavaBean
(了解)
// 导包,三个
// jackson-annotations-2.2.3.jar
// jackson-core-2.2.3.jar
// jackson-databind-2.2.3.jar
// 核心对象
OjbectMapper om = new ObjectMapper();
// 反序列化不常用
// 如果是普通类型 bean/普通list/map
T t = om.readValue(jsonStr,T.class);
// 如果泛型是自定义类型(弃用)
//T t = om.readValue(jsonStr, new TypeReference<T>(){});
4.5 案例演示
/*
JSON转换工具的使用
*/
public class ObjectMapperTest {
private ObjectMapper mapper = new ObjectMapper();
/*
1.User对象转json, json转User对象
json字符串 = {"name":"张三","age":23}
user对象 = User{name='张三', age=23}
*/
@Test
public void test01() throws Exception{
//User对象转json 响应时的序列化
User user = new User("张三",23);
String json = mapper.writeValueAsString(user);
System.out.println("json字符串:" + json);
//json转User对象
User user2 = mapper.readValue(json, User.class);
System.out.println("java对象:" + user2);
}
/*
2.map<String,String>转json, json转map<String,String>
json字符串 = {"姓名":"张三","性别":"男"}
map对象 = {姓名=张三, 性别=男}
*/
@Test
public void test02() throws Exception{
//map<String,String>转json
HashMap<String,String> map = new HashMap<>();
map.put("姓名","张三");
map.put("性别","男");
String json = mapper.writeValueAsString(map);
System.out.println("json字符串:" + json);
//json转map<String,String>
HashMap<String,String> map2 = mapper.readValue(json, HashMap.class);
System.out.println("java对象:" + map2);
}
/*
3.map<String,User>转json, json转map<String,User>
json字符串 = {"黑马一班":{"name":"张三","age":23},"黑马二班":{"name":"李四","age":24}}
map对象 = {黑马一班=User{name='张三', age=23}, 黑马二班=User{name='李四', age=24}}
*/
@Test
public void test03() throws Exception{
//map<String,User>转json
HashMap<String,User> map = new HashMap<>();
map.put("黑马一班",new User("张三",23));
map.put("黑马二班",new User("李四",24));
String json = mapper.writeValueAsString(map);
System.out.println("json字符串:" + json);
//json转map<String,User>
// HashMap<String,User> map2 = mapper.readValue(json,new TypeReference<HashMap<String,User>>(){});
HashMap<String,User> map2 = mapper.readValue(json, HashMap.class);
System.out.println("java对象:" + map2);
}
/*
4.List<String>转json, json转 List<String>
json字符串 = ["张三","李四"]
list对象 = [张三, 李四]
*/
@Test
public void test04() throws Exception{
//List<String>转json
ArrayList<String> list = new ArrayList<>();
list.add("张三");
list.add("李四");
String json = mapper.writeValueAsString(list);
System.out.println("json字符串:" + json);
//json转 List<String>
ArrayList<String> list2 = mapper.readValue(json,ArrayList.class);
System.out.println("java对象:" + list2);
}
/*
5.List<User>转json, json转List<User>
json字符串 = [{"name":"张三","age":23},{"name":"李四","age":24}]
list对象 = [User{name='张三', age=23}, User{name='李四', age=24}]
*/
@Test
public void test05() throws Exception{
//List<User>转json
ArrayList<User> list = new ArrayList<>();
list.add(new User("张三",23));
list.add(new User("李四",24));
String json = mapper.writeValueAsString(list);
System.out.println("json字符串:" + json);
//json转List<User>
ArrayList<User> list2 = mapper.readValue(json,ArrayList.class);
//ArrayList<User> list2 = mapper.readValue(json,new TypeReference<ArrayList<User>>(){});
System.out.println("java对象:" + list2);
}
}
5.综合案例一
优化细节1:修改触发事件
$("#username").keyup(function (e) {/*.....*/}
优化细节2:添加功能
// 通过事件委派为提示框的div绑定事件
// 添加:高亮切换时添加到输入框,背景黄色
$("#show").on("mouseover","div",function () {
$(this).css("background","yellow");
$("#username").val(this.innerText);
});
// 添加:移出时背景颜色还原
$("#show").on("mouseout","div",function () {
$(this).css("background","none");
});
// 添加:点击时提示框消失
$("#show").on("click","div",function () {
$("#show").hide();
});
完整代码
<script src="js/jquery-3.3.1.min.js"></script>
<script>
//1.为用户名输入框绑定鼠标键盘敲击事件
$("#username").keyup(function (e) {
//2.获取输入的用户名
let username = $("#username").val();
//3.判断用户名是否为空
if(username == null || username == "") {
//4.如果为空,将联想框隐藏
$("#show").hide();
return;
}
// 通过事件委派为提示框的div绑定事件
// 高亮切换时添加到输入框,背景黄色
$("#show").on("mouseover","div",function () {
$(this).css("background","yellow");
$("#username").val(this.innerText);
});
// 移出时背景颜色还原
$("#show").on("mouseout","div",function () {
$(this).css("background","none");
});
// 点击时提示框消失
$("#show").on("click","div",function () {
$("#show").hide();
});
//5.如果不为空,发送AJAX请求。并将数据显示到联想框
$.ajax({
//请求的资源路径
url:"userServlet",
//请求参数
data:{"username":username},
//请求方式
type:"POST",
//响应数据形式
dataType:"json",
//请求成功后的回调函数
success:function (data) {
//将返回的数据显示到show的div
let names = "";
for(let i = 0; i < data.length; i++) {
names += "<div>"+data[i].name+"</div>";
}
$("#show").html(names);
$("#show").show();
}
});
});
</script>
UserServlet
@WebServlet("/userServlet")
public class UserServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求和响应的编码
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
//1.获取请求参数
String username = req.getParameter("username");
//2.调用业务层的模糊查询方法得到数据
UserService service = new UserServiceImpl();
List<User> users = service.selectLike(username);
//3.将数据转成JSON,响应到客户端
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(users);
resp.getWriter().write(json);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
}
UserSeriveImpl
public class UserServiceImpl implements UserService {
@Override
public List<User> selectLike(String username) {
List<User> users = null;
SqlSession sqlSession = null;
InputStream is = null;
try{
//1.加载核心配置文件
is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过SqlSession工厂对象获取SqlSession对象
sqlSession = sqlSessionFactory.openSession(true);
//4.获取UserMapper接口的实现类对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//5.调用实现类对象的模糊查询方法
users = mapper.selectLike(username);
}catch (Exception e) {
e.printStackTrace();
}finally {
//6.释放资源
if(sqlSession != null) {
sqlSession.close();
}
if(is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//7.返回结果到控制层
return users;
}
}
UserMapper
public interface UserMapper {
/*
模糊查询
*/
@Select("SELECT * FROM user WHERE name LIKE CONCAT('%',#{username},'%') ORDER BY search_count DESC LIMIT 0,4")
public abstract List<User> selectLike(String username);
}
6.案例二
6.1 判断页面滚动
6.2 代码实现
index.html
<script>
//1.定义发送请求标记,如果上一次请求还没有处理完,就不要再发起新的请求了
// true表示可以发送请求,false反之
let send = true;
//2.定义当前页码和每页显示的条数
let start = 1;
let pageSize = 10;
//3. 冗余的一段距离,提前开始加载下一页数据
let bottom = 5;
//4.设置页面加载事件
// $(function () {
//5.为当前窗口绑定滚动条滚动事件
$(window).scroll(function () {
if (send) {
//6.获取必要信息,用于计算当前展示数据是否浏览完毕
//当前窗口的高度
let windowHeight = $(window).height();
//滚动条从上到下滚动距离
let scrollTop = $(window).scrollTop();
//当前文档的高度
let docHeight = $(document).height();
//7.计算当前展示数据是否浏览完毕
//当 滚动条距底部的距离 + 当前滚动条滚动的距离 + 当前窗口的高度 >= 当前文档的高度
if ((bottom + scrollTop + windowHeight) >= docHeight) {
//8.判断请求标记是否为true,表示上一次请求已经处理 完了,或者从来还没有发起过请求
// 这个时候可以发起新的请求
//9.将请求标记置为false,当前异步操作完成前,不能重新发起请求。
send = false;
//10.根据当前页和每页显示的条数来 请求查询分页数据
queryByPage(start, pageSize);
//11.当前页码+1
start++;
}
}
});
// });
//定义查询分页数据的函数
function queryByPage(start, pageSize) {
//加载动图显示
$(".loading").show();
//发起AJAX请求
$.ajax({
//请求的资源路径
url: "newsServlet",
//请求的参数
data: {"start": start, "pageSize": pageSize},
//请求的方式
type: "POST",
//响应数据形式
dataType: "json",
//请求成功后的回调函数
success: function (data) {
//加载动图隐藏
$(".loading").hide();
if (data.length == 0) {
$("#no").html("我也是有底线的...");
return;
}
//将数据显示
let titles = "";
for (let i = 0; i < data.length; i++) {
titles += "<li>\n" +
" <div class=\"title-box\">\n" +
" <a href=\"#\" class=\"link\">\n" +
data[i].title +
" <hr>\n" +
" </a>\n" +
" </div>\n" +
" </li>";
}
//显示到页面
$(".news_list").append(titles);
//将请求标记设置为true
// 到这里表示当前这次请求已经处理完成,后续的请求可以发送了,所以置为true
send = true;
}
});
}
</script>
NewsServlet.java
@WebServlet("/newsServlet")
public class NewsServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求和响应的编码
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
//1.获取请求参数
String start = req.getParameter("start");
String pageSize = req.getParameter("pageSize");
/* 程序健壮性判断
1. 防止为提交这两个值
2. 防止提交的值不符合格式要求
*/
int startNum = 1;
int pageSizeNum = 10;
try {
startNum = Integer.parseInt(start);
} catch (Exception e) {
e.printStackTrace();
}
try {
pageSizeNum = Integer.parseInt(pageSize);
} catch (Exception e) {
e.printStackTrace();
}
/* 程序健壮性判断结束 */
//2.根据当前页码和每页显示的条数来调用业务层的查询方法,得到分页Page对象
NewsService service = new NewsServiceImpl();
Page page = service.pageQuery(startNum, pageSizeNum);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
////3.将得到的数据转为JSON
//String json = new ObjectMapper().writeValueAsString(page);
//
////4.将数据响应给客户端
//resp.getWriter().write(json);
new ObjectMapper().writeValue(resp.getWriter(), page);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
}
6.3 案例二:页码分页
页面正文没有内容,但是发起请求后,会讲数据填充到正文的
div
中。填充时要覆盖html
,不要追加append
。控件效果不好,以后也不是我们用,理解就可以。
地址栏输入非法内容,不做健壮性判断就出问题了,测试人员负责。
开发人员选项,查看分页按钮是
超链接
。判断发送请求的参数根据按钮上的文本进行区分。