Axios
1、了解Axios
(一)了解Axios
一、特点
- 在浏览器端能发送ajax请求
- 能在node.js中发送http请求
- 支持promise的相关操作(API)
- 请求和响应的拦截器(能在请求之前做一些准备工作,在响应回来后对响应的结果做一些处理)
- 对请求和响应的数据做转换
- 取消请求
- 自动将结果转换成json格式数据
- 保护作用阻止XSRF攻击
官方文档:http://axios-js.com/zh-cn/docs/index.html
GIthub:https://github.com/axios/axios
如果测试,可以使用这个进行配合使用:npm install -g json-server
官网:https://github.com/typicode/json-server
2、安装
1)使用npm(包管理工具)最后通过打包工具引入(项目使用)
$ npm install axios
2)使用bower(包管理工具)一般在页面使用script标签引入
$ bower install axios
3)使用yarn(包管理工具)(项目使用)
$ yarn add axios
4 ) cdn导入:
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.0.0-alpha.1/axios.js"></script>
3、基本使用
1、axios发送请求方法
request、get、post、put、head、delete、options、patch等一系列http请求类型方法
2、axios请求响应结构
headers:响应头信息;
data:响应体的结果(服务器响应的结果,进行了json解析);
config:配置对象(包括了请求类型等等);
request:原生的ajax请求对象,也就是xhlhttprequest实例对象;
status:响应状态码,statusText:响应字符串
3、请求对象
可以查看官方文档:https://github.com/axios/axios
4、基本使用:(发送GET、POST、PUT、DELETE请求)
- 代码实现
<div class="container">
<h1 class="page-header">基本使用</h1>
<button class="btn btn-primary">发送 GET 请求</button>
<button class="btn btn-warning">发送 POST 请求</button>
<button class="btn btn-success">发送 PUT 请求</button>
<button class="btn btn-danger">发送 DELETE 请求</button>
</div>
<script>
const btns = document.querySelectorAll("button");
//请求绑定
btns[0].onclick = () => {
//发送 AJAX 请求 利用axios发送
//axios返回的是一个promise对象
axios({
//参数:请求类型
method: "GET",
//url
url: "http://localhost:3000/posts/2",
//还可以设置请求头信息等参数
}).then((response) => {
console.log(response);
});
};
//POST请求 添加一篇新的文章
btns[1].onclick = () => {
//发送 AJAX 请求 利用axios发送
//axios返回的是一个promise对象
axios({
//参数:请求类型
method: "POST",
//url
url: "http://localhost:3000/posts",
//还设置请求头
data: {
title: "sehunBBQ",
author: "小彬",
},
}).then((response) => {
console.log(response);
});
};
//put请求更新数据
btns[2].onclick = () => {
//发送 AJAX 请求 利用axios发送
//axios返回的是一个promise对象
axios({
//参数:请求类型
method: "PUT",
//url
url: "http://localhost:3000/posts/3",
//还设置请求头
data: {
title: "今天天气很好",
author: "小彬",
},
}).then((response) => {
console.log(response);
});
};
//删除数据
btns[3].onclick = () => {
//发送 AJAX 请求 利用axios发送
//axios返回的是一个promise对象
axios({
//参数:请求类型
method: "DELETE",
//url
url: "http://localhost:3000/posts/3",
}).then((response) => {
console.log(response);
});
};
</script>
其他使用:
基本使用
<script>
const btns = document.querySelectorAll("button");
//发送get请求
btns[0].onclick = () => {
//axios()方法发送
axios
.request({
method: "GET",
url: "http://localhost:3000/comments",
})
.then((response) => {
console.log(response);
});
};
//发送post请求
btns[1].onclick = () => {
//axios()方法发送
axios
.post("http://localhost:3000/comments", {
body: "hello",
postId: 2,
})
.then((response) => {
console.log(response);
});
};
</script>
4、基本知识
- axios的默认配置
<body>
<div class="container">
<h2 class="page-header">基本使用</h2>
<button class="btn btn-primary"> 发送GET请求 </button>
<button class="btn btn-warning" > 发送POST请求 </button>
<button class="btn btn-success"> 发送 PUT 请求 </button>
<button class="btn btn-danger"> 发送 DELETE 请求 </button>
</div>
<script>
//获取按钮
const btns = document.querySelectorAll('button');
//默认配置
axios.defaults.method = 'GET';//设置默认的请求类型为 GET
axios.defaults.baseURL = 'http://localhost:3000';//设置基础 URL
axios.defaults.params = {id:100};
axios.defaults.timeout = 3000;//
btns[0].onclick = function(){
axios({
url: '/posts'
}).then(response => {
console.log(response);
})
}
</script>
</body>
- axios实例对象
<body>
<div class="container">
<h1 class="page-header">基本使用</h1>
<button class="btn btn-primary">发送 GET 请求</button>
<button class="btn btn-warning">发送 POST 请求</button>
</div>
</body>
<script>
//获取按钮
const btns = document.querySelectorAll("button");
//创建实例对象
//段子接口 https://api.apiopen.top/gegetJoke
const passage = axios.create({
baseURL: "https://api.apiopen.top",
timeout: 2000,
});
const another = axios.create({
baseURL: "https://b.cm",
timeout: 2000,
});
//passage 与axios对象功能几乎是一样的
//当做函数
/*
passage({
url: "/getJoke",
}).then((response) => {
console.log(response);
});
*/
//借助封装好的方法
passage.get("/getJoke").then((response) => {
console.log(response.data);
});
</script>
3.axios拦截器:
请求拦截器(在发送请求前,使用函数对请求的参数和内容进行处理和检测,若请求有问题可直接进行拦截->取消,后进先执行=则后面的请求拦截器先执行)
响应拦截器(对响应的结果预处理,先进先执行=前面的响应拦截器先执行)
1)请求拦截器:
① 在真正发送请求前执行的回调函数
② 可以对请求进行检查或配置进行特定处理
③ 失败的回调函数,传递的默认是error
④ 成功的回调函数,传递的默认是config(也必须是)
2)响应拦截器
① 在请求得到响应后执行的回调函数
② 可以对响应数据进行特定处理
③ 成功的回调函数,传递的默认是response
④ 失败的回调函数,传递的默认是error
3)请求转换器:对请求头和请求体数据进行特定处理的函数
响应转换器:将响应体json字符串解析为js对象或数组的函数
4)代码
<script>
//设置一个请求拦截器,在请求拦截器中可以对请求参数进行修改
//config:配置对象
axios.interceptors.request.use(
function (config) {
console.log("请求拦截器 成功 1号");
// config.headers.test = "I am only a header!";
//修改 config 中的参数
config.params = { a: 100 };
return config;
},
(error) => {
console.log("请求拦截器 失败 1号");
return Promise.reject(error);
}
);
axios.interceptors.request.use(
function (config) {
console.log("请求拦截器 成功 2号");
// config.headers.test = "I am only a header!";
//修改 config 中的参数
config.timeout = 2000;
return config;
},
(error) => {
console.log("请求拦截器 失败 2号");
return Promise.reject(error);
}
);
//设置一个响应拦截器,可以对响应结果做一些处理
axios.interceptors.response.use(
function (response) {
console.log("响应拦截器 成功 1号");
return response;
},
function (error) {
console.log("响应拦截器 失败 1号");
return Promise.reject(error);
}
);
//设置一个响应拦截器
axios.interceptors.response.use(
function (response) {
console.log("响应拦截器 成功 2号");
return response;
},
function (error) {
console.log("响应拦截器 失败 2号");
return Promise.reject(error);
}
);
//发送请求
axios({
method: "GET",
url: "http://localhost:3000/posts",
})
.then((response) => {
console.log("自定义回调处理成功的结果");
//console.log(response);
})
.catch((reason) => {
console.log(reason);
});
</script>
4.取消请求:终端启动是输入json-server --watch db.json -d +时间(如 -d 2000)
<body>
<div class="container">
<h1 class="page-header">axios取消请求</h1>
<button class="btn btn-primary">发送请求</button>
<button class="btn btn-warning">取消请求</button>
</div>
</body>
<script>
//获取按钮
const btns = document.querySelectorAll("button");
//2.声明一个全局变量
let cancel = null;
//发送请求
btns[0].onclick = () => {
//检测上一次请求是否已经完成
if (cancel !== null) {
//则代表上一次请求还未取消,故直接取消上一次请求
cancel();
}
axios({
method: "GET",
url: "http://localhost:3000/posts",
//1.添加配置对象的属性
cancelToken: new axios.CancelToken((c) => {
//3.将c的值赋值给cancel
cancel = c;
}),
}).then((response) => {
console.log(response);
//当请求执行完后 将cancel进行初始化设置
cancel = null;
});
};
//取消请求
btns[1].onclick = () => {
cancel();
};
</script>
5、源码
1、axios发送请求
<script>
//构造函数
function Axios(config) {
//初始化
this.defaults = config; //为了创建default默认属性
this.interceptors = {
request: {},
response: {},
};
}
//原型添加相关方法
Axios.prototype.request = function (config) {
console.log("发送AJAX请求,请求类型为" + config.method);
};
Axios.prototype.get = function (config) {
return this.request({ method: "GET" }); //在内部调用了request方法
};
Axios.prototype.post = function (config) {
return this.request({ method: "POST" });
};
//声明函数
function createInstance(config) {
//1.实例化一个对象
let context = new Axios(config);
//可使用Axios中的方法 如context.get(),但是不能当做函数使用
//创建请求对象函数
let instance = Axios.prototype.request.bind(context);
//instance是一个函数,可以instance({}),此时instance不能 instance.get()
//将 Axios.prototype对象中的方法添加到instance函数中
Object.keys(Axios.prototype).forEach((element) => {
instance[element] = Axios.prototype[element].bind(context);
});
//为 instance 函数对象添加属性 default 与 interceptors
Object.keys(context).forEach((key) => {
//进行赋值
instance[key] = context[key];
});
return instance;
}
//创建对象
let axios = createInstance({ method: "GET" });
//发送请求
axios.get();
</script>
2、axios请求发送过程
<script>
//axios 发送请求 axios Axios.prototype.request bind
//1.声明构造函数
function Axios(config) {
this.config = config;
}
Axios.prototype.request = (config) => {
//发送请求
//创建了一个promise对象
let promise = Promise.resolve(config);
//声明一个数组 undefine 作用:占位
let chains = [dispatchRequest, undefined];
//调用then方法指定回调
let result = promise.then(chains[0], chains[1]);
//返回promise结果
return result;
};
//2.dispatchRequset函数
function dispatchRequest(config) {
//调用适配器发送请求
return xhrAdapter(config).then(
(response) => {
//对响应结果进行处理
return response;
},
(error) => {
throw error;
}
);
}
//3.adapter适配器
function xhrAdapter(config) {
return new Promise((resolve, reject) => {
//发送AJAX请求
let xhr = new XMLHttpRequest();
//初始化
xhr.open(config.method, config.url);
//发送
xhr.send();
//绑定事件
xhr.onreadystatechange = () => {
//判断成功的条件
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
//成功的状态
resolve({
//配置对象
config: config,
//响应体
data: xhr.response,
//响应头
headers: xhr.getAllResponseHeaders(), //字符串
//xhr的请求对象
request: xhr,
//相应的状态码
status: xhr.status,
//响应状态字符串
statusText: xhr.statusText,
});
} else {
//失败的状态
reject(new Error("请求失败 失败的状态码为" + xhr.status));
}
}
};
});
}
//4.创建axios函数
let axios = Axios.prototype.request.bind(null);
axios({
method: "GET",
url: "http://localhost:3000/posts",
}).then((response) => {
console.log(response);
});
</script>
3、axios拦截器模拟实现
<script>
//构造函数
function Axios(config) {
this.config = config;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager(),
};
}
//发送请求 难点与重点
Axios.prototype.request = function (config) {
//创建一个 promise 对象
let promise = Promise.resolve(config);
//创建一个数组
const chains = [dispatchRequest, undefined];
//处理拦截器
//请求拦截器 将请求拦截器的回调 压入到 chains 的前面 request.handles = []
this.interceptors.request.handlers.forEach((item) => {
chains.unshift(item.fulfilled, item.rejected); //往数组前去添加
});
//响应拦截器
this.interceptors.response.handlers.forEach((item) => {
chains.push(item.fulfilled, item.rejected); //往数组后添加
});
// console.log(chains);
//遍历
while (chains.length > 0) {
promise = promise.then(chains.shift(), chains.shift());
}
return promise;
};
//发送请求
function dispatchRequest(config) {
//返回一个promise 队形
return new Promise((resolve, reject) => {
resolve({
status: 200,
statusText: "OK",
});
});
}
//创建实例
let context = new Axios({});
//创建axios函数
let axios = Axios.prototype.request.bind(context);
//将 context 属性 config interceptors 添加至 axios 函数对象身上
Object.keys(context).forEach((key) => {
axios[key] = context[key];
});
//拦截器管理器构造函数
function InterceptorManager() {
this.handlers = [];
}
InterceptorManager.prototype.use = function (fulfilled, rejected) {
this.handlers.push({
fulfilled,
rejected,
});
};
//以下为功能测试代码
// 设置请求拦截器 config 配置对象
axios.interceptors.request.use(
function one(config) {
console.log("请求拦截器 成功 - 1号");
return config;
},
function one(error) {
console.log("请求拦截器 失败 - 1号");
return Promise.reject(error);
}
);
axios.interceptors.request.use(
function two(config) {
console.log("请求拦截器 成功 - 2号");
return config;
},
function two(error) {
console.log("请求拦截器 失败 - 2号");
return Promise.reject(error);
}
);
// 设置响应拦截器
axios.interceptors.response.use(
function (response) {
console.log("响应拦截器 成功 1号");
return response;
},
function (error) {
console.log("响应拦截器 失败 1号");
return Promise.reject(error);
}
);
axios.interceptors.response.use(
function (response) {
console.log("响应拦截器 成功 2号");
return response;
},
function (error) {
console.log("响应拦截器 失败 2号");
return Promise.reject(error);
}
);
//发送请求
axios({
method: "GET",
url: "http://localhost:3000/posts",
}).then((response) => {
console.log(response);
});
</script>
4、axios取消请求
<body>
<div class="container">
<h1 class="page-header">axios取消请求</h1>
<button class="btn btn-primary">发送请求</button>
<button class="btn btn-warning">取消请求</button>
</div>
</body>
<script>
//构造函数
function Axios(config) {
this.config = config;
}
//原型 request 方法
Axios.prototype.request = function (config) {
return dispatchRequest(config);
};
//dispatchRequest 函数
function dispatchRequest(config) {
return xhrAdapter(config);
}
//xhrAdapter
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();
//将整体结果设置为失败
cancel = null;
reject(new Error("请求已经被取消"));
});
}
});
}
//创建 axios 函数
const context = new Axios({});
const axios = Axios.prototype.request.bind(context);
//CancelToken 构造函数
function CancelToken(executor) {
//声明一个变量
var resolvePromise;
//为实例对象添加属性
this.promise = new Promise((resolve) => {
//将 resolve 赋值给 resolvePromise
resolvePromise = resolve;
});
//调用 executor 函数
executor(function () {
//执行 resolvePromise 函数
resolvePromise();
});
}
//获取按钮 以上为模拟实现的代码
const btns = document.querySelectorAll("button");
//2.声明全局变量
let cancel = null;
//发送请求
btns[0].onclick = function () {
//检测上一次的请求是否已经完成
if (cancel !== null) {
//取消上一次的请求
cancel();
}
//创建 cancelToken 的值
let cancelToken = new CancelToken(function (c) {
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>
6、总结
1.axios和Axios的关系
1)从语法上来说:axios不是Axios的实例;
2)从功能上来说:axios是Axios的实例;(因为axios拥有Axios实例对象上的方法);
3)axios是Axios.prototype.request函数bind()返回的函数;
4)axios作为对象有Axios原型对象上的所有方法,有Axios对象上的所有属性;
2.instance与axios的区别
1)相同:
① 都是一个能发任意请求的函数:request(config);
② 都有发特定请求的各种方法:get()/post()/put()/delete();
③ 都有默认配置和拦截器属性:defaults/interceptors;
2)不同:
① 默认配置可能不一样;
② instance不具备axios后面添加的一些方法:creat()/CancelToken()/all();
3.axios运行的整体流程
request(config) -> dispatchRequest(config) -> xhrAdapter(config)(适配器)
扩展
Object.keys()的详解和用法
在实际开发中,我们有时需要知道对象的所有属性;ES5
引入了Object.keys
方法,成员是参数对象自身的(不含继承的)所有可遍历( enumerable
)属性的键名。
传入对象,返回属性名
var data={a:1,b:2,c:9,d:4,e:5};
console.log(Object.keys(data));//["a", "b", "c", "d", "e"]
Object.keys(data).map((key,item)=>{
console.log(key,data[key]);//key=>属性名 data[key]=>属性值
});
传入字符串,返回索引
var str = 'ab1234';
console.log(Object.keys(obj)); //[0,1,2,3,4,5]
传入数组 返回索引
var arr = ["a", "b", "c"];
console.log(Object.keys(arr)); // console: ["0", "1", "2"]
构造函数 返回空数组或者属性名
function Pasta(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
this.toString = function () {
return (this.name + ", " + this.age + ", " + this.gender);
}
}
console.log(Object.keys(Pasta)); //console: []
var spaghetti = new Pasta("Tom", 20, "male");
console.log(Object.keys(spaghetti)); //console: ["name", "age", "gender", "toString"]
扩展
Object.values()
Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历( enumerable )属性的键值。
Object.entries()
Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历( enumerable )属性的键值对数组。