AJAX--XMLHttpRequest & 跨域
XMLHttpRequestXMLHttpRequest 是浏览器内置的一个构造函数作用:基于 new 出来的 XMLHttpRequest 实例对象,可以发起 Ajax 的请求。axios 中的 axios.get()、axios.post()、axios() 方法,都是基于 XMLHttpRequest(简称:XHR) 封装出来的!使用 XMLHttpRequest 发起 GET 请求主要的
XMLHttpRequest
XMLHttpRequest 是浏览器内置的一个构造函数
作用:基于 new 出来的 XMLHttpRequest 实例对象,可以发起 Ajax 的请求。
axios 中的 axios.get()、axios.post()、axios() 方法,都是基于 XMLHttpRequest(简称:XHR) 封装出来的!
使用 XMLHttpRequest 发起 GET 请求
主要的 4 个实现步骤:
创建 xhr 对象
调用 xhr.open() 函数
调用 xhr.send() 函数
监听 load 事件
示例
<script>
// 1 创建 xhr对象
const xhr = new XMLHttpRequest()
// 2 调用 open方法 指定 请求类型,url
xhr.open('get', 'http://www.itcbc.com:3006/api/getbooks')
// 3 发送出去 send
xhr.send()
// 4 监听onload 数据响应事件
xhr.addEventListener('load', function(){
// console.log(this.response);
// 字符串转对象 可用可不用
const obj = JSON.parse(this.response)
console.log(obj);
})
</script>
请求时携带URL参数 或 提交请求体
URL参数,只能拼接在 URL 地址 后面
请求体,放到 send() 里面
提交请求体数据,需指定Content-Type头
当需要提交请求体数据时,需要在 xhr.open() 之后,调用 xhr.setRequestHeader() 函数,指定请求体的编码格
<script>
const xhr = new XMLHttpRequest();
xhr.open('post', 'http://www.itcbc.com:3006/api/addbook');
// post 三种不同数据格式的参数
// 1 a=b&c=d 同时也需要指定 content-type 才行!!
// 2 对象格式 {a:"b",c:"d"} 同时也需要指定 content-type 才行!!
// 3 formdata 数据
const data = {
bookname: '2从入门到入土2',
author: '我自己',
publisher: '新闻出版社',
appkey: 'wanshao1234',
};
// 设置对应的 content-type
xhr.setRequestHeader("Content-type","application/json");
const str =JSON.stringify(data);
xhr.send(str); // 传递 a=b&c=d
xhr.addEventListener('load', function () {
console.log(this.response);
});
</script>
数据交换格式
数据交换格式,就是服务器端与客户端之间数据传输的格式。
两种数据交换格式: XML(很少用) JSON(主流)
JSON
JSON(全称:JavaScript Object Notation)是一种数据交换格式,它本质上是用字符串的方式来表示对象或数组类型的数据。例如:
JSON 数据
用字符串的方式来表示的对象或数组类型的数据,叫做 JSON 数据。
JSON 数据的格式有两种: 对象格式 数组格式
JSON 的语法要求
使用 JSON 定义 JSON 格式的数据时,要遵守以下的 6 条规则:
属性名必须使用双引号包裹
字符串类型的值必须使用双引号包裹
JSON 中不允许使用单引号表示字符串
JSON 中不能写注释
JSON 的最外层必须是对象或数组格式(其他类型也可以,但多数是对象或数组格式)
不能使用 undefined 或函数作为 JSON 的值
对象格式的 JSON 数据
对象格式的 JSON 数据,最外层使用 { } 进行包裹,内部的数据为 “key”: “value” 的键值对结构。其中:
key 必须使用英文的双引号进行包裹
value 的值只能是字符串、数字、布尔值、null、数组、对象类型(可选类型只有这 6 种)
数组格式的 JSON 数据
数组格式的 JSON 数据,最外层使用 [ ] 进行包裹,内部的每一项数据之间使用英文的 , 分隔。其中: 每一项的值类型只能是字符串、数字、布尔值、null、数组、对象这 6 种类型之一。
把 JSON 数据转换为 JS 数据
调用浏览器内置的 JSON.parse() 函数,可以把 JSON 格式的字符串转换为 JS 数据,例如:
把 JS 数据转换为 JSON 数据
调用浏览器内置的 JSON.stringify() 函数,可以把 JS 数据转换为 JSON 格式的字符串,例如:
序列化和反序列化
把真实数据转换为字符串的过程,叫做序列化
把字符串转换为真实数据的过程,叫做反序列化
把 XMLHttpRequest 请求到的 JSON 数据反序列化为 JS 对象
在 xhr 对象的 load 事件中,通过 xhr.response 访问到的是 JSON 格式的字符串数据。可以调用 JSON.parse() 函数将 xhr.response 转化为 JS 对象。示例代码如下:
补充 - JSON 文件
后缀名是 .json 的文件叫做 JSON 文件,专门用来存储 JSON 格式的数据。例如:
注意: .json 结尾的文件,一般都是项目的配置文件
后面的 Node.js 中也会使用到 package.json 这个配置文件
封装自己的 Ajax 函数
处理 data 请求体数据 - 判断 options.data 是否为 FormData 格式
如何判断一个对象是否为某个构造函数的实例?
答案:对象 instanceof 构造函数
如何判断某个值是否为字符串类型? 答案:typeof 值 === ‘string’
如何判断某个值是否为对象类型? 答案:typeof 值 === ‘object’
ajax代码的封装
<script src="./lib/jquery.js"></script>
<script> // 使用了 axios 来发送ajax请求
// 我们应该使用原生的ajax代码 来封装成 axios 调用模样 axios({url,method,params,data}).then || promise(有点东西-node)
// jq中 也是封装过 ajax代码
$.ajax({
url: 'http://www.itcbc.com:3006/api/getbooks',
// type: 'get',
type: 'post',
// data: {
// appkey: 'wanshao1234',
// },
data: 'appkey=wanshao1234',
success(result) {
console.log(result);
},
});
/*
1 type 可能是get 也可能是post
2 data 3种(查询字符串 json formdata )
3 希望封装的代码 什么样式
ajax({url,type,data,success})
{
url: 'http://www.itcbc.com:3006/api/getbooks',
type: 'post',
data: "appkey=wanshao1234",
success(result) {
console.log(result);
}
*/ </script>
<script>
/*
1 ajax 是一个函数(正解) 还是 一个对象
2 它接受一个 参数 格式 什么格式(对象)
3 需要在ajax函数中 写完 整个原生ajax 发送请求的代码
*/
const obtion = {
url:'http://www.itcbc.com:3006/api/getbooks',
type:'get',
data:'appkey=lin123',
success(result){
// result 等于 要等于响应的数据 = 对象格式
console.log(result);
}
}
// 里面方法 option.success(2);
axios(obtion)
function axios(config){
const xhr = new XMLHttpRequest()
xhr.open(config.type,config.url + '?' + config.data)
xhr.send()
xhr.addEventListener('load',function(){
// 响应的数据 this.response
// console.log(this.response);
const obj = JSON.parse(this.response)
config.success(obj)
})
}
</script>
ajax代码的封装.-get-不携带参数
<script>
const option = {
url: 'http://www.itcbc.com:3006/api/getbooks',
type: 'get',
// data: 'appkey=wanshao1234',
success(result) {
console.log(result);
},
};
ajax(option);
ajax({
url: 'http://www.itcbc.com:3006/api/getbooks',
type: 'get',
data: 'appkey=wanshao1234',
success(result) {
console.log(result);
},
});
function ajax(config) {
console.log(config.data);
// 封装的时候考虑到用户 (可能带参数 , 可能不带参数)
const xhr = new XMLHttpRequest();
if (config.data) {
// 有传递参数
xhr.open(config.type, config.url + '?' + config.data);
} else {
xhr.open(config.type, config.url);
}
xhr.send();
xhr.addEventListener('load', function () {
const obj = JSON.parse(this.response);
config.success(obj);
});
}
</script>
ajax代码的封装.-get-不携带参数
<script>
// 形参默认值
// 解构
// function func(msg="hello") {
// console.log(msg);
// }
// func();// 输出默认值
// func('你好'); /// 你好
// const option = {
// type: 'get',
// data: 'a=1',
// };
// const { data } = option;
// console.log(data); // a=1
// function func(config) {
// const {data}=config;
// console.log(data);
// }
// function func({ data = '1' ,type}) {
// // 解构 同时也设置了默认值
// // console.log(data);
// console.log(type);
// }
// func(option);
const option = {
url: 'http://www.itcbc.com:3006/api/getbooks',
type: 'get',
// data: 'appkey=wanshao1234',
success(result) {
console.log(result);
},
};
ajax(option);
ajax({
url: 'http://www.itcbc.com:3006/api/getbooks',
type: 'get',
data: 'appkey=wanshao1234',
success(result) {
console.log(result);
},
});
function ajax({ url, type, data = '', success }) {
// 封装的时候考虑到用户 (可能带参数 , 可能不带参数)
const xhr = new XMLHttpRequest();
xhr.open(type, url + '?' + data);
// 如果 data没有值 url = http://www.itcbc.com?
// 如果 data有值 url = http://www.itcbc.com?appkey=wanshao1234
xhr.send();
xhr.addEventListener('load', function () {
const obj = JSON.parse(this.response);
success(obj);
});
}
</script>
ajax代码的封装.-get-对象格式的参数
<script>
// 1
const option = {
url: 'http://www.itcbc.com:3006/api/getbooks',
type: 'get',
success(result) {
console.log(result);
}
}
ajax(option)
// 2
ajax({
url: 'http://www.itcbc.com:3006/api/getbooks',
type: 'get',
data: 'appkey=lin123',
success(result) {
console.log(result);
}
})
// 3
ajax({
url: 'http://www.itcbc.com:3006/api/getbooks',
type: 'get',
data: {
appkey: 'lin123',
bookname: '人生长恨水长东',
},
success(result) {
console.log(result);
}
})
function ajax({ url, type, data = '', success }) {
const xhr = new XMLHttpRequest()
// 判断方式写法一
// if (typeof data === 'object') {
// data = new URLSearchParams(data)
// data.toString()
// }
// 判断方式写法2
if (typeof data === 'object') {
// data是一个对象
data = new URLSearchParams(data).toString();
}
// 判断方式写法3
// (typeof data === 'object')&&(data = new URLSearchParams(data).toString())
xhr.open(type, url + '?' + data)
xhr.send()
xhr.addEventListener('load', function () {
const obj = JSON.parse(this.response)
success(obj)
})
}
ajax代码-post
<script>
ajax({
url: 'http://www.itcbc.com:3006/api/getbooks',
type: 'get',
success(result) {
console.log(result);
},
});
ajax({
url: 'http://www.itcbc.com:3006/api/getbooks',
type: 'post',
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);
xhr.send();
}
xhr.addEventListener('load', function () {
const obj = JSON.parse(this.response);
success(obj);
});
}
</script>
封装一个Ajax 函数,调用它可以发起 GET 或 POST 请求:
<input type="file" accept="image/*" />
<script> /*
判断当前data的数据类型
1 字符串类型
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xhr.send(data); // 传递 a=b&c=d
2 对象类型
xhr.setRequestHeader("Content-type","application/json");
const str =JSON.stringify(data);
xhr.send(str); // 传递 a=b&c=d
3 formdata
xhr.send(formdata);
*/
// 判断当前数据 字符串类型 typeof
// console.log( typeof data === "string" ); // 字符串类型
// console.log( typeof data === "object" ); // 对象类型
// console.log(typeof data);
// 判断你儿子 是不是亲生
// 儿子 instanceof 爸爸
// 实例 instanceof 构造函数
// console.log( data instanceof FormData );
// console.log(data instanceof Array);
const input = document.querySelector('input');
input.addEventListener('change', function () {
const file = this.files[0];
const formdata = new FormData();
formdata.append('avatar', file);
ajax({
url: 'http://www.itcbc.com:3006/api/formdata',
type: 'post',
data:formdata,
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>
防抖 & 节流
防抖(debounce)指的是:频繁触发某个操作时,只执行最后一次。
防抖的应用场景
场景:搜索框只在输入完后,才执行查询的请求。
好处:这样可以有效减少请求的次数,节省网络资源。
/*
防抖 防止抖动
1 用在输入框中 实现 不用用户按下回车键 就发送请求
2 技术原理
1 用新的一次输入来清除上一次的延时器
2 同时开启一个新的延时器
*/
getData();
// change事件 输入框的值发生改变-输入框失去焦点 才触发
// input 事件
// 定义一个 演示器 id
let timeid; // 钻 石 城 堡
const input = document.querySelector('input');
input.addEventListener('input', function (event) {
clearTimeout(timeid);
// 开启了一个延时器 里面代码 1s后会执行
timeid = setTimeout(function () {
const value = input.value.trim();
const queryStr = `?bookname=${value}`;
getData(queryStr);
}, 1000);
});
节流
节流(throttle)指的是:单位时间内,频繁触发同一个操作,只会触发 1 次。
/*
节流
上一次的业务没有结束的话 不允许开启下一次业务
使用场景 移动端分页 - 倒计时按钮 等等
*/
// 这一次请求还没有结束 就不能开启下一个请求
// 业务 分页 业务
// 开关
let isLoadding = false; // 有没有请求在发送当中
// 点击按钮的时候先判断 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;
});
}
同源策略 & 跨域
同源指的是两个 URL 地址具有相同的协议、主机名、端口号。
例如,下表给出了相对于 www.test.com/index.html 页面的 5 个同源检测结果:
同源策略(英文全称 Same origin policy)是浏览器提供的一个安全功能。
浏览器的同源策略规定:不允许非同源的 URL 之间进行资源的交互。
同源指的是两个 URL 的协议、主机名、端口号完全一致,反之,则是跨域。
出现跨域的根本原因:浏览器的同源策略不允许非同源的 URL 之间进行资源的交互。例如:
网页:http:// www.test.com / index.html
接口:http:// www.api.com /userlist
受到同源策略的限制,上面的网页请求下面的接口会失败!
浏览器对跨域请求的拦截过程
浏览器允许发起跨域请求。但跨域请求回来的数据,会被浏览器拦截,无法被页面获取到!示意图如下:
突破浏览器跨域限制的两种方案
JSONP 和 CORS 是实现跨域数据请求的两种技术方案。
注意:目前 JSONP 在实际开发中很少会用到,CORS 是跨域的主流技术解决方案
CORS 的概念
CORS 是解决跨域数据请求的终极解决方案,全称是 Cross-origin resource sharing。
CORS 技术需要浏览器和服务器同时支持,二者缺一不可:
浏览器要支持 CORS 功能(主流的浏览器全部支持,IE 不能低于 IE10)
服务器要开启 CORS 功能(需要后端开发者为接口开启 CORS 功能)
CORS 的原理
服务器端通过 Access-Control-Allow-Origin 响应头,来告诉浏览器当前的 API 接口是否允许跨域请求。
CORS 的两个主要优势
CORS 是真正的 Ajax 请求,支持 GET、POST、DELETE、PUT、PATCH 等这些常见的 Ajax 请求方式
只需要后端开启 CORS 功能即可,前端的代码无须做任何改动
JSONP
JSONP 是实现跨域数据请求的一种技术解决方案。它只支持 GET 请求,不支持 POST、DELETE 等其它请求。
在实际开发中很少被使用 在面试中可能会问到 JSONP 的原理
JSONP 不是真正的 Ajax 技术
在解决跨域问题时:
CORS 方案用到了 XMLHttpRequest 对象,发起的是纯正的 Ajax 请求
JSONP 方案没有用到 XMLHttpRequest 对象,因此,JSONP 不是真正的 Ajax 技术
结论:只要用到了 XMLHttpRequest 对象,发起的就是 Ajax 请求!
JSONP 的底层实现原理
JSONP 在底层,用到了 < script> 标签的 src 属性!
原因:
< script> 标签的 src 属性,不受浏览器同源策略的限制
可以把非同源的 JavaScript 代码请求到本地,并执行
JSONP 的底层实现原理 - P1
把非同源的 JavaScript 代码请求到本地,并执行:
JSONP 的底层实现原理 - P2
如果请求回来的 JavaScript 代码只包含函数的调用,则需要程序员手动定义 show 方法。示例代码如下:
缺点:服务器响应回来的代码中,调用的函数名是写死的!
JSONP 的底层实现原理 - P3
在指定 < script> 标签的 src 属性时,可以通过查询参数中的 callback,自定义回调函数的名称:
JSONP 的底层实现原理 - P4
指定 < script> 标签的 src 属性时,还可以通过查询参数的方式,指定要发送给服务器的数据:
示例
<!-- 随便加载别人公司的js文件 !! -->
<!-- <script src="./utils/04.js"></script> -->
<script> // 前端拿到后端的数据
// 希望前端自己来定义show方法的业务
function show(option) {
alert('我的数据拿到啦' + option.message);
} </script>
<!-- 自己来定义 后端起的函数名称 show 函数 自己来具体的业务 -->
<script src="http://www.itcbc.com:3006/api/getscript2"></script>
<!-- <script> // show里面的数据 就是后端想要给我们返回的数据
show({ message: '你必须自己定义好叫做show的函数,否则就会出错' }); </script> -->
<script>
// option后端想要给前端传递数据
function show(option) {
console.log('数据回来了, 后端帮我们调用的show 里面有数据');
console.log(option);
}
/*
1 跨域的目的 拿到后端的数据 (前端和后端 跨域 不同源 直接发送ajax请求 失败)
2 script标签的src属性不受同源策略的影响 跨域
3 浏览器对于script的支持
1 允许它下载指定路径的js代码 (js代码 由后端程序员来设定它内容)
show({data:['a','b','c']}) => show 后端自己命名 - show中传递了真正的数据
2 而且还会执行 js代码
你前端需要自己来定义 show方法的内容 你前端的业务逻辑
*/
// show();
// let data = 1233;
// show();
</script>
<!-- 根据后端要求写的请求js的代码 -->
<script src="http://www.itcbc.com:3006/api/getscript2"></script>
更多推荐
所有评论(0)