Ajax XMLHttpRequest & 跨域: 一.XMLHttpRequest: XMLHttpRequest是浏览器内置的一个构造函数 作用:基于 new 出来的 XMLHttpRequest 实例对象,可以发起 Ajax 的请求。 axios 中的 axios.get()、axios.post()、axios() 方法,都是基于 XMLHttpRequest(简称:XHR) 封装出来的! 使用 XMLHttpRequest 发起 GET 请求: 主要的 4 个实现步骤:
创建 xhr 对象
调用 xhr.open() 函数
调用 xhr.send() 函数
监听 load 事件
<script>
let xhr = new XMLHttpRequest();
xhr.open('GET', 'http://xxx.com/api/xx');
xhr.send();
xhr.addEventListener('load', function () {
console.log(this.response);
})
</script>
请求时携带URL参数 或 提交请求体 :
URL参数,只能拼接在 URL 地址 后面 请求体,放到 send() 里面
<script>
let xhr = new XMLHttpRequest();
// 将请求参数拼接到url后面
xhr.open('请求方式', 'http://www.itcbc.com/api/xx?id=1&username=zhangsan');
xhr.send(请求体);
xhr.addEventListener('load', function () {
console.log(this.response);
})
</script>
提交请求体数据,需指定Content-Type头: 当需要提交请求体数据时,需要在 xhr.open() 之后,调用 xhr.setRequestHeader() 函数,指定请求体的编码格式
// 将请求参数拼接到url后面
xhr.open('POST', 'http://www.itcbc.com/api/post');
// 根据请求体格式的不同,需设置对应的Content-Type头
xhr.setRequestHeader('Content-Type', '值')
xhr.send('username=zs&age=20');
xhr.addEventListener('load', function () {
console.log(this.response);
})
</script>
请求体格式 和 对应的Content-Type值: 为了方便服务器接收数据,当提交请求体时,需要指定一个叫做Content-Type的请求头
| 请求体格式 | Content-Type | 是否需要在代码中指定 |
|---|---|---|
| 参数=值&参数=值 | application/x-www-form-urlencoded | 是 |
| '{ "id": 1, "name": "zs" }' | application/json | 是 |
| new FormData() | multipart/form-data; xxxxxxxxx随机字符 | 否,浏览器自动设置 |
请求体格式Content-Type是否需要在代码中指定参数=值&参数=值application/x-www-form-urlencoded是'{ "id": 1, "name": "zs" }'application/json是new FormData()multipart/form-data; xxxxxxxxx随机字符否,浏览器自动设置 例:xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') 使用axios就不用关心这个请求头了,因为axios已经帮我们处理好了加请求头这件事,不过写原生代码,就需要自己指定了 二.数据交换格式: 数据交换格式,就是服务器端与客户端之间数据传输的格式 两种数据交换格式:
XML(很少用) JSON(主流)
什么是 JSON:
JSON(全称:JavaScript Object Notation)是一种数据交换格式,它本质上是用字符串的方式来表示对象或数组类型的数据
JSON 数据的格式有两种:对象格式 数组格式
JSON 的语法要求
属性名必须使用双引号包裹
字符串类型的值必须使用双引号包裹
JSON 中不允许使用单引号表示字符串
JSON 中不能写注释
JSON 的最外层必须是对象或数组格式(其他类型也可以,但多数是对象或数组格式)
不能使用 undefined 或函数作为 JSON 的值
对象格式的 JSON 数据,最外层使用 { } 进行包裹,内部的数据为 "key": "value" 的键值对结构:
key 必须使用英文的双引号进行包裹 value 的值只能是字符串、数字、布尔值、null、数组、对象类型(可选类型只有这 6 种)
数组格式的 JSON 数据
最外层使用 [ ] 进行包裹,内部的每一项数据之间使用英文的 , 分隔。其中: 每一项的值类型只能是字符串、数字、布尔值、null、数组、对象这 6 种类型之一。
调用浏览器内置的 JSON.parse() 函数,可以把 JSON 格式的字符串转换为 JS 数据
调用浏览器内置的 JSON.stringify() 函数,可以把 JS 数据转换为 JSON 格式的字符串
序列化和反序列化:
把真实数据转换为字符串的过程,叫做序列化 调用 JSON**.stringify()** 函数进行 JSON 的序列化
把字符串转换为真实数据的过程,叫做**反序列化 ** 调用 JSON.parse() 函数进行 JSON 的反序列化
把 XMLHttpRequest 请求到的 JSON 数据反序列化为 JS 对象: 在 xhr 对象的 load 事件中,通过 xhr.response 访问到的是 JSON 格式的字符串数据。可以调用 JSON.parse() 函数将 xhr.response 转化为 JS 对象
<script>
let xhr = new XMLHttpRequest();
// 将请求参数拼接到url后面
xhr.open('请求方式', 'http://www.itcbc.com/api/xx?id=1&username=zhangsan');
xhr.send(请求体);
xhr.addEventListener('load', function () {
let res = JSON.parse(this.response);//需要将xhr.response 转化为 JS 对象
console.log(res);
})
</script>
补充 - JSON 文件: 后缀名是 .json 的文件叫做 JSON 文件,专门用来存储 JSON 格式的数据。 注意:.json 结尾的文件,一般都是项目的配置文件 三.封装自己的 Ajax 函数: 定义ajax函数的参数选项: 自定义的 Ajax 函数,它接收一个配置对象作为参数。配置对象中包含如下 5 个参数选项:
| 参数选项 | 说明 |
|---|---|
| method | 请求的类型(GET 或 POST) |
| url | 请求的 URL 地址 |
| data | 请求体数据,有三种格式,分别是(FormData 格式、JSON 格式、普通字符串格式) |
| success | 请求成功之后的回调函数 |
参数选项说明method请求的类型(GET 或 POST)url请求的 URL 地址data请求体数据,有三种格式,分别是(FormData 格式、JSON 格式、普通字符串格式)success请求成功之后的回调函数 实现步骤:
创建 xhr 实例对象 根据用户指定的 method 和 url 发起对应的请求 把响应的 JSON 数据转换为 JS 对象 调用 success 成功的回调,把请求的结果当作参数传递进去
注意事项:
有get post 请求分开类型写 data数据有三种类型 string object formdata 区分开 post请求 xhr.setRequestHeader设置对应的请求头的 Content-Type 类型 当data=FormData时 instanceof 的运用 实例 instanceof 构造函数
请求通用代码如下: ', data: { publisher: '黑马出版社', appkey: 'lijiahao123' }, success(result) { console.log(result); } }) function ajax({ url, type, data = '', success }) { const xhr = new XMLHttpRequest(); // 判断 请求类型 if (type === 'get') { // get请求的相关的代码 if (typeof data === 'object') { data = new URLSearchParams(data).toString(); } xhr.open(type, url + '?' + data); xhr.send(); } else if (type === 'post') { // post请求的相关的代码 xhr.open(type, url);
// 判断是不是字符串
if (typeof data === 'string') {
xhr.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhr.send(data);
} else if (typeof data === 'object') {
// 判断是不是对象
// 判断是不是 FormData 实例
if (data instanceof FormData) {
// 是 FormData 实例
xhr.send(data);
} else {
// 普通的对象
xhr.setRequestHeader('Content-type', 'application/json');
const str = JSON.stringify(data);
xhr.send(str); // 传递 a=b&c=d
}
}
}
xhr.addEventListener('load', function () {
const obj = JSON.parse(this.response);
success(obj);
});
}
</script>
四.同源策略 & 跨域: 同源: 同源指的是两个 URL 地址具有相同的协议、主机名、端口号 例如,下表给出了相对于 www.test.com/index.html 页面的 5 个同源检测结果:
URL是否同源原因说明www.test.com/other.html是同源(协议、域名、端口相同)www.test.com/about.html否协议不同(http 与 https)blog.test.com/movie.html否域名不同(www.test.com 与 blog.test.com)www.test.com:7001/home.html否端… 80 端口与 7001 端口)www.test.com:80/main.html是同… 同源策略: 同源策略(英文全称 Same origin policy)是浏览器提供的一个安全功能。 浏览器的同源策略规定:不允许非同源的 URL 之间进行资源的交互。 例:
www.escook.cn/index.html 同源,接口调用成功 www.escook.cn/api/login www.escook.cn/index.html 非同源,接口调用失败 www.liulongbin.top/api/login
跨域: 同源指的是两个 URL 的协议、主机名、端口号完全一致,反之,则是跨域。 出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互。
网页:www.test.com/index.html 接口:www.api.com/userlist 受到同源策略的限制,上面的网页请求下面的接口会失败!
浏览器允许发起跨域请求。但跨域请求回来的数据,会被浏览器拦截,无法被页面获取到 突破浏览器跨域限制的两种方案: JSONP 和 CORS 是实现跨域数据请求的两种技术方案
| 方案 | 诞生的时间 | 方案来源 | 优点 | 缺点 |
|---|---|---|---|---|
| JSONP | 出现较早 | 民间(非官方) | 兼容性好(兼容低版本 IE) | 仅支持 GET 请求 |
| CORS | 出现较晚 | W3C 官方标准 | 支持 GET、POST、PUT、DELETE、PATCH等常见的请求方式 | 不兼容某些低版本浏览器 |
方案诞生的时间方案来源优点缺点JSONP出现较早民间(非官方)兼容性好(兼容低版本 IE)仅支持 GET 请求CORS出现较晚W3C 官方标准支持 GET、POST、PUT、DELETE、PATCH等常见的请求方式不兼容某些低版本浏览器 注意:目前 JSONP 在实际开发中很少会用到,CORS 是跨域的主流技术解决方案 CORS 的两个主要优势:
CORS 是真正的 Ajax 请求,支持 GET、POST、DELETE、PUT、PATCH 等这些常见的 Ajax 请求方式 只需要后端开启 CORS 功能即可,前端的代码无须做任何改动
JSONP: JSONP 是实现跨域数据请求的一种技术解决方案。它只支持 GET 请求,不支持 POST、DELETE 等其它请求
在实际开发中很少被使用 在面试中可能会问到 JSONP 的原理
在解决跨域问题时:
CORS 方案用到了 XMLHttpRequest 对象,发起的是纯正的 Ajax 请求 JSONP 方案没有用到 XMLHttpRequest 对象,因此,JSONP 不是真正的 Ajax 技术
结论:只要用到了 XMLHttpRequest 对象,发起的就是 Ajax 请求! JSONP 的底层实现原理: JSONP 在底层,用到了 原因:
JSONP 的底层实现原理:
把非同源的 JavaScript 代码请求到本地,并执行:
如果请求回来的 JavaScript 代码只包含函数的调用,则需要程序员手动定义 show 方法。
<script src="http: //www.itcbc.com:3006/api/getscri…">
缺点:服务器响应回来的代码中,调用的函数名是写死的
在指定 时,可以通过查询参数中的 callback,自定义回调函数的名称:
<script src="http: //www.itcbc.com:3006/api/jsonp?c…
在指定 时,还可以通过查询参数的方式,指定要发送给服务器的数据:
<script src="http: //www.itcbc.com:3006/api/jsonp?c…">
五.防抖 & 节流: 防抖: 防抖(debounce)指的是:频繁触发某个操作时,只执行最后一次 防抖的应用场景: 场景:搜索框只在输入完后,才执行查询的请求。 好处:这样可以有效减少请求的次数,节省网络资源。 核心代码: ; getData(queryStr); }, 1000); });
节流: 节流(throttle)指的是:单位时间内,频繁触发同一个操作,只会触发 1 次。 节流的应用场景: 射击游戏中,单位时间内只能发射一颗子弹。 核心代码:
// 点击按钮的时候先判断 isLoadding true还是false
// true 请求在发送中 return
// false 没有请求
// 先设置 isLoadding true
// 发送请求出去
// 请求回来了 设置 isLoadding = false
document.querySelector('button').addEventListener('click', function () {
if (isLoadding) {
return;
}
isLoadding = true;
// 发送请求的时候 禁用按钮
// this.disabled=true;
getData();
});
function getData(query = '') {
console.log('请求发送出去');
axios({
method: 'get',
url: 'http://www.itcbc.com:3006/api/getbooks' + query,
// params:{},
}).then((result) => {
console.log('数据回来了');
// document.querySelector('button').disabled=false
isLoadding = false;
});
}
</script>