异步请求(ajax):发送服务器请求额外数据而不刷新页面,从而实现更好的用户体验。
XMLHttpRequest
XMLHttpRequest对象:为发送服务器请求和获取响应提供了合理的接口。这个接口可以实现异步从服务器获取额外数据,用户点击不同刷新页面页可以获取数据。
简单使用
var xhr = new XMLHttpRequest()
xhr.onreadyStateChange = function () {
if (xhr.readystate === 4) {
if (xhr.status === 304 || (xhr.status >= 200 && xhr.status < 300)) {
console.log('type: success, result: ', xhr.responseText)
} else {
console.log('type: error, errCode:', xhr.status)
}
}
}
xhr.open('get', 'example.php', true)
xhr.setRequestHeader('testHeader', 'testHeaderVal')
xhr.send(null)
xhr属性
| 属性 | 描述 |
|---|---|
| readyState | 返回xhr的状态 0:xhr对象一创建,但尚未调用open()方法 1:open()方法已经被调用 2:send()方法已经被调用,并且头部和状态已经可获得 3:请求中,responseText属性已经包含部分属性 4:请求已完成 |
| response | 返回响应的正文 |
| responseText | 返回文本 |
| responseType | 用于指定响应中包含的数据类型: xhr.responseType = 'json'/'text'/'document' |
| responseURL | 获取返回响应的序列化URL |
| responseXML | 返回一个包含请求检索的html或xml的Document |
| status | 返回响应的数值状态码: status状态码与http状态码相同 |
| statusText | 返回响应状态的文本信息。 |
| timeout | 设置请求的超时时间, |
| upload |
xhr方法
| 方法 | 描述 |
|---|---|
| open(method,url,isAsync) | 初始化一个新创建的请求 |
| setRequestHeader(header,value) | 设置请求头 |
| send() | 用于发送http请求 接受一个参数做为请求体 如果请求方法为get和head,应将请求体设置为null |
| abort() | xhr.abort():终止请求 |
| getAllResponseHeaders() | 获取所有响应头 |
| getAllResponseHeader() | 获取单个响应头的值 |
xhr事件
| 事件 | 描述 |
|---|---|
| onreadystatechange | 当readystate状态改变时触发 |
| ontimeout | 响应超时时触发 |
| onabort | 请求终止时触发 |
| onerror | 请求错误时触发 |
| onloadstart | 接收到第一个响应开始触发 |
| onload | 请求完成时触发 |
| onprogress | 请求中周期性触发 |
Axios
axios是一个基于promise的网络请求库,可以用于浏览器和node.js
axios的特点
- 基于promise的异步ajax请求库
- 浏览器端和node都可以使用
- 支持请求、响应拦截器
- 支持请求取消
- 请求和响应的数据转换
- 支持发送多个请求
axios的使用
axios(config): 通用/最本质的发任意类型请求的方式
axios.request(config): 等同于 axios(config)
axios.get(url[, config]): 发 get 请求
axios.delete(url[, config]): 发 delete 请求
axios.post(url[, data, config]): 发 post 请求
axios.put(url[, data, config]): 发 put 请求
axios.defaults.xxx: 请求的默认全局配置
axios.interceptors.request.use(): 添加请求拦截器
axios.interceptors.response.use(): 添加响应拦截
axios.create([config]): 创建一个新的 axios
axios.Cancel(): 用于创建取消请求的错误对象
axios.CancelToken(): 用于创建取消请求的 token 对象
axios.isCancel(): 是否是一个取消请求的错误
axios.all(promises): 用于批量执行多个异步请求
axios.spread(): 用来指定接收所有成功数据的回调函数的方法
具体使用详情,请参考:github.com/axios/axios…
拦截器使用
// 请求拦截器
axios.interceptors.request.use(function (config) {
// 发送请求之前的配置
return config;
}, function (error) {
// 发送请求失败之后的操作
return Promise.reject(error);
});
// 响应拦截器
axios.interceptors.response.use(function (response) {
// 请求成功之后,对响应数据进行操作
return response;
}, function (error) {
// 失败之后的操作
return Promise.reject(error);
});
注意:请求拦截器后定义的会先执行,具体原因请看拦截器内部实现原理。
拦截器功能实现
拦截器:拦截器本质对象,对象里面存放是一个数组,里面存放一个对象(fulfilled,rejected)对象,分别表示promise成功和失败的回调;
拦截器实例对象上包含的方法:
- use:添加拦截器
- eject:删除拦截器
function InterceptorManager(){
this.handlers = [] // 保存拦截器
}
// 定义use 添加拦截器的方法
InterceptorManager.prototype.use = functioon(fulfilled,rejected){
this.handlers.push({
fulfilled,
rejected
})
return this.handlers.length - 1;
}
// 定义删除拦截器的方法
InterceptorManager.prototype.eject = functioon(id){
if(this.handlers[id]) this.handlers[id] = null;
}
Axios类功能实现
Axios类主要用于:
-
设置发送请求的配置
-
设置拦截器
-
设置请求方法request
创建请求链,本质是一个数组,有请求拦截器,请求方法,响应拦截器方法组成
通过promise链调用请求链
最后返回promise
-
在原型对象上封装get,post等请求方法
const dispatchRequest = "请求方法" // 可能是xhr或者http
function Axios(defaults){
this.defaults = defaults;// 设置默认配置
this.interceptors = { // 设置拦截器
request:new InterceptorManager(),
response:new InterceptorManager()
}
}
// 在原型对象上挂载原始请求方法
Axios.prototype.request = function requset(config){
// 合并配置,暂时不用关注,配置主要用于真实发送请求的文件中
config = mergeConfig(this.defaults,config);
// 创建拦截器中间键,我称之为请求链,undefined主要起到占位的作用,因为定义拦截器是方法都是成对出现的。
let chain = [dispatcRequest,undefined];
// 将请求拦截器加到请求链的前面,因为请求拦截器是往数组头部添加元素所以后定义在会先执行。
this.interceptors.request.forEach(item=>{
chain.unshift(item.fuifilled,item,rejected);
})
// 将响应拦截器加到请求链的后面
this.interceptors.request.forEach(item=>{
chain.push(item.fuifilled,item,rejected);
})
// 创建一个成功的promise,并且设置成功的值为config对象(设置promise起点)
let promise = Promise.resolve(config);
// 开始指向chain
while(chain.length > 0){
promise = promise.then(chain.shift(),chain.shift());
}
// 最后返回promise对象
return promise;
}
axios对象的实现
axios对Axios类再次进行封装,是用户可以更加灵活使用。
- 创建Axios对象context
- 将Axios实例对象上的request方法,绑定到context上并返回型函数instance
- 将Axios实例对象上的方法绑定到instance上。
- 将context对象上的属性绑定到instance上
function createInstance(defaults){
const context = new Axios(defaults);
const instance = context.prototype.request.bind(context);// 将request方法返回,内部this指向context
for(let key in Axios.prototype){
instance[key] = Axios.prototype[key].bind(context);
}
for(let key in context){
instance[key] = conetxt[key];
}
return instance;
}
// axio是函数,但也是对象上面有get,post,requset等方法和拦截器属性等
const axios = createInstance(defaults); // 此时返回的axios就是请求库暴露出的唯一接口axios
// axios添加属性Axios构造函数
axios.Axios = Axios;
// 添加一个工厂函数,用于返回不同配置的axios对象
axios.create = function(config){
return createInstance(mergeConfig(default,config));
}
// 添加可取消选项
axios.Cancel
axios.CancelToken
axios.isCancel
// 用于并发请求
axios.all = function all(promises){
return Promise.all(promise);
}
axios.spread = function spread(cb){
return function(arr){
return cb.apply(null,arr);
}
}
axios取消请求使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>取消请求</title>
<link crossorigin='anonymous' href="https://cdn.bootcss.com/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script src="./node_modules/axios/dist/mine-axios.js"></script>
</head>
<body>
<div class="container">
<h2 class="page-header">axios取消请求</h2>
<button class="btn btn-primary"> 发送请求 </button>
<button class="btn btn-warning"> 取消请求 </button>
</div>
<script>
//获取按钮
const btns = document.querySelectorAll('button');
//2.声明全局变量
let cancel = null;
//发送请求
btns[0].onclick = function(){
//检测上一次的请求是否已经完成
if(cancel !== null){
//取消上一次的请求
cancel();
}
//创建 cancelToken 的值
let cancelToken = new axios.CancelToken(
function(c){
//3. 将 c 的值赋值给 cancel
cancel = c;
});
axios({
method: 'GET',
url: 'http://localhost:3000/posts',
//1. 添加配置对象的属性
cancelToken: cancelToken
}).then(response => {
console.log(response);
//将 cancel 的值初始化
cancel = null;
})
}
//绑定第二个事件取消请求
btns[1].onclick = function(){
cancel();
}
</script>
</body>
</html>
axios取消请求使用
-
通过CancelToken.source工厂函数
const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // cancel the request (the message parameter is optional) source.cancel('Operation canceled by the user.'); -
通过回调函数
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel = c; }) }); // cancel the request cancel();
axios是如何实现取消请求
// 请求函数
function xhrAdapter(config){
//发送 AJAX 请求
return new Promise((resolve, reject) => {
//实例化对象
const xhr = new XMLHttpRequest();
//初始化
xhr.open(config.method, config.url);
//发送
xhr.send();
//处理结果
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
//判断结果
if(xhr.status >= 200 && xhr.status < 300){
//设置为成功的状态
resolve({
status: xhr.status,
statusText: xhr.statusText
});
}else{
reject(new Error('请求失败'));
}
}
}
//关于取消请求的处理
if(config.cancelToken){
//对 cancelToken 对象身上的 promise 对象指定成功的回调
config.cancelToken.promise.then(value => {
xhr.abort();
//将整体结果设置为失败
reject(new Error('请求已经被取消'))
});
}
})
}
// 取消请求函数
//CancelToken 构造函数
function CancelToken(executor){
//声明一个变量
var resolvePromise;
//为实例对象添加属性
this.promise = new Promise((resolve) => {
//将 resolve 赋值给 resolvePromise
resolvePromise = resolve
});
//调用 executor 函数
executor(function(){
//执行 resolvePromise 函数
resolvePromise();
});
}
// 工厂函数。
CancelTOken.source = function source(){
let cancel; // 定义取消函数
let token = new CancelToken(function(c){
cancel = c;
})
return {
token:token,
cancel:cancel
}
}