面试还不会 Ajax?拿来把你!
Ajax(Asynchronous Javascript And XML)
最近想着去找个面试,然后看到 Ajax 也是考点之内(面向关键字面试),虽然已经用了很久的 axios 但没有系统的学过 Ajax,所以就有今天这篇文章。
背景介绍
AJAX 全称(Async Javascript and XML)
即异步的 JavaScript 和 XML,是一种创建交互式网页应用的网页开发技术,可以在不重新加载整个网页的情况下,与服务器交换数据,并且更新部分网页
为什么需要 Ajax?如果你用过 JSP 就知道,在 jsp 中你需要返回数据或者需要和服务器交互的时候是要刷新页面的,比如一个主页,你只是需要加载某个 tab 的数据,比如衣物栏
你可能要付出加载整个页面其他元素的代价,像搜索框对应的 jsp 文件,或者其它的,都要重新传。
现在你很难想象你进入到一个购物商城页面之后,本来主页已经很耗费时间了,然后随便点击某个商品很有可能会花费和进入主页相同的时间,用户不会等待这么久。
对于前端开发来说,jsp 的效率也不是很高,就以我不久前被裹挟着开发了一次 jsp 网上购物商城为例,HTML 是后端返回的,即使在 Chrome 修改了样式文件,仍然需要重启后端(重开 SpringBoot 的时间要很久)你才能够真正看到实际环境下的 HTML 页面,也就是没有热更新;每一次的路由跳转也需要重新加载页面,等待时间大于开发时间。
说了这么多的 jsp 还是回归正题 ajax,ajax 就解决了上面这些缺点,局部更新,减少了用户的等待时间,不必每一次与服务器的交互都要重新加载,无刷新获取数据。
实现
Ajax 的原理简单来说通过 XMLHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后用 JavaScript 来操作 DOM 而更新页面
请求/响应
XMLHttpRequest
过程是
- 创建 new XMLHttpRequest
- 建立连接 open
- 发送请求 send
- 回调 onreadystatechange
简单介绍一下 XMLHttpRequest 的上面的这些 API
// 1
const xhr = new XMLHttpRequest();
// 2
xhr.open(method, url, [async][, user][, password])
// 3
xhr.send([body])
// 4
xhr.onreadystatechange = function () {...}
下面是一个 JS 使用 XMLHttpRequest 对象发送请求的一个简单例子
如果你不会本地起个接口,可以去百度一个测试接口网站
//1. 创建对象
const xhr = new XMLHttpRequest();
//2. 初始化 设置请求方法和 url
xhr.open("GET", "http://127.0.0.1:8000/server?a=100&b=200&c=300");
//3. 发送
xhr.send();
//4. 事件绑定 处理服务端返回的结果
// on 当....时候
// readystate 是 xhr 对象中的属性, 表示状态 0 1 2 3 4
// change 改变
xhr.onreadystatechange = function () {
//判断 (服务端返回了所有的结果)
if (xhr.readyState === 4) {
//判断响应状态码 200 404 403 401 500
// 2xx 成功
if (xhr.status >= 200 && xhr.status < 300) {
//处理结果 行 头 空行 体
//响应
// console.log(xhr.status);//状态码
// console.log(xhr.statusText);//状态字符串
// console.log(xhr.getAllResponseHeaders());//所有响应头
// console.log(xhr.response);//响应体
} else {
}
}
};
fetch
fetch 据说是以后会替代 ajax 的东西,但现在比较常用的库 axios 并不是基于 fetch 的,但也算比较重要吧?抄一段尚硅谷的代码做示例,感兴趣的可以去看一下 MDN 关于 fetch 的文档
fetch("http://127.0.0.1:8000/fetch-server?vip=10", {
//请求方法
method: "POST",
//请求头
headers: {
name: "atguigu",
},
//请求体
body: "username=admin&password=admin",
})
.then((response) => {
// return response.text();
return response.json();
})
.then((response) => {
console.log(response);
});
超时/重复请求
上面的都是简单的使用,面试也不会让你手撕一个 XMLHttpRequest 请求吧?,所以接下来就讲一下 Ajax 需要注意的几个点。
这里为什么要将超时和重复请求放在一起,因为面向用户来说重复请求大多是因为超时,当然超时和重复请求的场景和原因可能有很多种。
如何处理超时?
XMLHttpRequest 本身就提供了超时相关的 API,使用 timeout 已经 ontimeOut() 可以面临超时的问题,如何在实践中测试超时可以使用 chrome 的控制台,具体可以看这篇文章,chrome 中几种网络限制解释和网络控制,引流-chrome 怎么控制网速
const xhr = new XMLHttpRequest();
xhr.timeout = 2000;
//超时回调
xhr.ontimeout = function () {
alert("网络异常, 请稍后重试!!");
};
如何处理重复请求?
重复请求可能远比我想的要复杂,面对的场景有很多,后续会再写一篇文章介绍重复请求
解决重复请求的意义,减小服务器的压力,因为现在后端的代码可能需要做大量的计算,或者一些数据库操作,所以一次请求可能有 1 - 3 秒,或者因为服务器太拉,对于用户来说 1s 都等不了,发现有长时间的置空状态可能会多次重复操作,比如点击购买按钮,发现没跳转到支付界面,这个时候可能就会再点一次,第二次还没有就第三次。。。。
当然也可以避免多次相同的请求返回多次响应的问题,频繁的弹出响应弹窗,也可以避免后端数据混乱,有很多网站没有做重复请求处理,导致返回相同的数据,比如下拉刷新两次返回两次刷新内容
抢购案例也是重复请求需要解决的问题,用户会多次点击购买按钮,前端需要判断请求是否完成
具体操作如下
// 当然是用一个信号量了
// 老 PV 操作了
let xhr = null;
let isSending = false; // 是否正在发送AJAX请求
...
// 如果正在发送, 则取消该请求, 创建一个新的请求
// abort() 是 xhr 取消请求的方法
if (isSending) xhr.abort();
xhr = new XMLHttpRequest();
//修改标识变量的值
isSending = true;
xhr.open("GET", "http://127.0.0.1:8000/delay");
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
//修改标识变量
isSending = false;
}
};
IE 缓存
基本不需要关注,了解即可
我也没有去验证过,缓存其实很重要,但这里的缓存针对的是 IE,对于图片等静态资源,但如果普通的 JSON 也缓存的话就会出现问题
跨域
这个老重要,我经常碰到,我碰到的跨域场景可以总结如下
- 第一次搭建后端,不会配置
- 不熟悉的语言有不同的配置跨域,又得百度
- 需要携带 Cookie
具体解决方法可以看一下思否的一个答案,具体是下面的方法
- jsonp,只支持 GET 请求
- CORS,这个是后端帮助你配置,这种跨域问题最好是交给后端,毕竟没有学习成本,自己一把梭的项目可以考虑
- webpack proxy + nginx
还有其他的解决方法,比如在修改你的 Chrome 让它可以跨域,不过对于面试可能不太会问这种
请求/响应扩展
前端开发中不一定都是 get/post 请求,现在后端还有一些其他请求,比如 put/delete/patch,这是偶然看到的某个面试题,其实这涉及到了 RESTful 架构,具体可以去阮一峰老师的博客去了解一下 理解 RESTful 架构,但作者本人从很多网站上看到的更多是 POST 请求一把梭。
注意:XMLHttpRequest 支持上述所有 HTTP 请求方法,一共是 9 种
- GET
- HEAD
- POST
- PUT
- DELETE
- CONNECT
- OPTIONS
- TRACE
- PATCH
封装
因为 XMLHttpRequest 写的是比较麻烦的,所以有一些库对它做了封装,比如 Jquery、axios,Jquery 用的比较少就不讲了,而 axios 它是一个比较新的库,它是基于 XMLHttpRequest 对象的封装。
为什么不使用 fetch,我简单的查了一下看到有人说是 browser and node.js 通用,当然还有更多的比较和考虑,有兴趣的可以参考 https://github.com/axios/axios/issues/3899
它对 XMLHttpRequest 的封装还是有点东西的,下面的面试题也提到了它,会具体的讲一下它的功能和优势。
面试题
我直接上面试题
- ajax 原理是什么?如何实现?同类型(请详细描述 AJAX 的工作原理)
原理可以上面已经讲的很清楚了,可以回去再阅读一下 - ajax 请求地址只支持 http/https 吗?能做到让它支持 rtmp://等其它自定义协议吗 ?
ajax 只支持 http/https 协议,可以通过自定义 http 头来间接支持自定义协议,我找半天也没有找到类似案例,太难了,PASS 掉 - 说说防止重复发送 ajax 请求的方法有哪些?各自有什么优缺点?
1. 防抖 2. 节流 3. 加锁,上面的 ajax 中有讲过加锁的方法,而对于防抖和节流应该是针对短时间内大量调用 ajax 方法的事件处理比较好,比如连续点击抢购按钮的点击事件可以使用防抖节流,不过考虑到绝大多数按钮绑定的点击事件其实也就是一个请求,所以直接在 ajax 上用也是一样的 - ajax 如何接收后台传来的图片?
这题考察的是 XMLHttpRequest.response 的返回类型,可以是 ArrayBuffer、Blob、Document,或 DOMString,如果是图片,将 Blob 转换一下就可以变成页面可以展示的图片(百度有很多案例),所以答案是 1.设置 responseType 为 Blob,2.将 Blob 保存为文件