简述
欢迎来到新的一篇,axios篇,axios是一个基于Promise的网络请求库,它并不是vue中的插件,所以在使用时
并不需要Vue.use(axios)
,讲axios之前说一下promise是什么,下面是本篇的主要内容:
Promise的基本使用
在JavaScript的世界中,所有代码都是单线程执行的。由于这个“缺陷”,导致JavaScript的所有网络操作,
浏览器事件,都必须是异步执行。因为网络请求这些操作需要时间,如果同步执行,用户不得不等待网络请求完毕才能得到响应。
像ajax,定时器等等,就是异步操作,这些异步操作会在将来调用我们提供的回调函数,但是Ajax这种异步操作
写起来太难看了,如果网络请求多了起来,后一个网络请求需要前一个网络请求的结果,那么代码就会形成回调地狱
所以Promise就出现了,它是ES6提供的对象,能更优雅的写异步请求,避免掉回调地狱这种情况。
下面是Promise的具体写法:
new Promise((resolve,reject)=>{
const flag=true
setTimeout(()=>{
console.log("执行定时任务");
if (flag){
resolve("异步操作执行成功");
}else {
reject("异步操作执行失败")
}
},1000);
}).then(res=>{ //箭头函数,只有一个参数时,()可以省略
console.log(res);
}).catch(error=>{
console.log(error);
})
/*
promise对象有两个参数,resolve,reject,参数名随意啊,我想说的是,这两个参数都是函数
并且是then,catch方法中的回调函数,我们提供的,当执行resolve方法时,就会执行then中的方法
当执行reject方法时,就会执行catch中的方法。
*/
上面的案例还是挺简单的,下面看看复杂一点的,在基本使用上做了一些简化操作
new Promise(resolve=>{ //如果只用resolve方法,则reject参数可以省略
setTimeout(()=>{
resolve("Hello");
},1000)
}).then(res=>{ //res就是Hello
console.log("第零次的处理");
return new Promise(resolve=>{ //返回一个promise对象后,继续使用then处理
setTimeout(()=>{
resolve(res+" Wo")
},1000)
})
}).then(res=>{ //res就是HelloWo
console.log('第一次的处理');
return Promise.resolve(res+" World") //对上面进行简化,直接调用resolve方法
}).then(res=>{ //res接收到HelloWoWorld
console.log('第二次的处理');
return res+" giao" //直接返回字符串,对上面再一次简化,相当于 return Promise.resolve(res+" Giao")
}).then(res=>{
console.log(res); //输出HelloWoWorldGiao
}).catch(err=>{ //上面代码没有执行reject,这里不执行
console.log(err);
})
/*
提示下,只要一个函数返回promise对象,那么我在调用这个函数后,就可以紧跟着调用then方法或catch方法
并不一定说then,catch方法和promise对象放在一起,比如在axios中就有相应的用法,vuex篇我们也使用过。
*/
发出网络请求方式有哪些
传统的ajax是基于XMLHttpRequest(XHR),但是在开发中并不使用,太繁琐
Jquery中的ajax代替传统的ajax,这是以前的情况,但是vue项目中,vue本身1W+行,Jquery也是1w+行
所有完全没必要为了用网络请求就引用这个重量级库,目前三大框架都有自己的网络请求模块,所以都不使用这种方式vue-resource,是vue1.0时vue官方推出的网络请求框架,但在vue2.0时被淘汰了,官方选择了axios
还有一种,应该说是一种请求方式,而不是网络请求框架,jsonp
使用jsonp最主要的原因往往是为了解决跨域访问的问题,不过只能解决get方式的跨域访问
ajax因为浏览器的同源策略,不能进行跨域请求,而jsonp是通过<script>
标签的src来帮助我们请求数据
jsonp的方式是请求时url后跟上一个callback参数,该参数值是一个回调函数的函数名,我们指定服务器返回的JS函数名称
函数的参数就是后端响应的数据,前端通过script标签的src发送请求,请求完成后,就可以使用这个数据。
下面是个伪代码://前端 <script src="http://www.keyi.world/getUser?callback="getUserInfo""></script> //回调函数 getUserInfo(res){ //该函数被回调,res接收到服务器传递的数据,即"真实数据" //对res进行处理 } //后端,后端返回的数据是一个函数,并在函数的参数中返回真实数据,相当于调用 return "getUserInfo("真实数据")" //这也就解释了为什么jsonp只能处理get请求
axios,内部基于promise对ajax进行了封装,不仅可以在浏览器中发出ajax,还可以在node.js中发送http请求
你还可以拦截请求和响应,在发出请求前后做一些处理。
axios的基本使用
安装axios,运行时依赖:
npm install axios -S
配置跨域
网络请求不可避免的会产生跨域问题,在项目根目录下创建vue.config.js文件,配置跨域如下:module.exports = { devServer: { proxy: { '/home': { //请求的代称,写在Axios里的BaseUrl target: 'http://api.qingyunke.com', ws: true, changeOrigin: true, pathRewrite: { '^/home': '' // '^/home': '/home' } } } } } /* "/home",就是配置一个url前缀,当在请求url中的端口号后加上这个/home, 就表示该请求是跨域请求就会将请求发给target指定的url中 如果发送请求时不加上这个url前缀,则会去找前端项目的public目录下的资源,即该请求不被本地服务器代理 ws属性表示,是否支持webSocket,默认为true changeOrigin属性表示,是否暴露请求头的host字段,如果为false,则请求后端时,后端获取的host值是真实的 如果为true,则表示隐藏自己的host字段,后端接收的host值是自己的服务器host值,默认值为true pathRewrite属性表示,当我们通过代理访问请求后,因为设置了url前缀,我们的访问路径变成了:/home/api.php, 但是我们实际的api.php页面使用/api.php请求才能被访问到,所以通过pathRewrite路径重写,将请求前缀去除 */
在main.js中使用axios
import axios from 'axios' //将url前缀设置为默认路径,这里的配置会将所有请求都会加上url前缀,即所有请求都被代理 axios.defaults.baseURL ='/home'; axios({ url: "/api.php", //如果没有指定method属性,则默认使用get请求,这里直接跟baseURL后面的地址即可 params: { //param就是url中?后跟的参数 key: 'free', appid: 0, msg: '你好吗' } }).then(res=>{ //axios内部返回了promise,所以这里调用then方法,能拿到返回结果 console.log(res); }) /* axios请求方式还有很多,下面是具体方式: 1)axios(config),默认情况下,如果只有url,则请求方式是get,设置method可以设置请求方式 2)axios.request(config) 3)axios.get(url[,config]) []表示可选 4)axios.delete(url[,config]) 5)axios.head(url[,config]) 4)axios.post(url[,data[,config]]) 4)axios.put(url[,data[,config]]) 4)axios.patch(url[,data[,config]]) */
axios发出并发请求
发送两个请求,两个请求都拿到结果再去做其他事情,案例如下,估计内部也是使用了promise的all方法
//baseURL,跨域等配置同上
axios.all([
axios({
url: "/api.php",
params: {
key: 'free',
appid: 0,
msg: '你叫什么名字'
}
}),
axios({
url: "/api.php",
params: {
key: 'free',
appid: 0,
msg: '杭州天气怎么样'
}
})
]).then(res=>{
console.log(res); //返回结果是数组,数组元素就是两个请求的结果
})
axios全局配置
全局配置指的是,抽离请求中的一些固定部分,例如,请求前缀(协议+域名+端口号),请求头设置一些固定值等等
原始配置:
axios({
url: "http://api.qingyunke.com/api.php",
timeout: 5000, //超时时间
params: { //请求参数
key: 'free',
appid: 0,
msg: '你好吗'
}
}).then(res=>{
console.log(res);
})
配置抽离:
//这里配置的是全局,你也可以在axios内部配置baseURL,跟上面例子中/home是一样的
axios.defaults.baseURL ='http://api.qingyunke.com'
axios.defaults.timeout=5000
axios({
//baseURL: ''
url: "/api.php",
params: { //请求参数
key: 'free',
appid: 0,
msg: '你好吗'
}
}).then(res=>{
console.log(res);
})
/*
那么,axios中传入的config对象中除了以上url,param,baseURL,timeout属性外,还可以有哪些属性?
请求地址:url: '/user'
请求类型:method: 'get'
请求根路径:baseURL: 'http://www.keyi.world'
请求前的数据处理:transformRequest: [function(data){}] //可以传入多个函数
请求后的数据处理:transformResponse: [function(data){}]
自定义的请求头:headers:{'x-Request-With':'XMLHttpRequest'}
URL查询对象:params:{id:12} //就是url携带的参数
查询对象序列化函数:paramsSerializer: function(params){}
request body:data:{key:'aa'}
超时设置:timeout:1000
跨域是否带token:withCredentials:false
自定义请求处理:adapter:function(resolve,reject,config){}
身份验证信息:auth:{uname:'',pwd:'12'}
响应式的数据格式:responseType:'json' //有json/raw/document/arraybuffer/text/stream
*/
axios的实例
我们从axios模块中导入对象后,使用的实例是默认的实例,当给该实例设置一些默认配置时,这些配置就被固定下来了
但是后续开发中,某些配置可能会不太一样,比如某些请求需要使用特定的baseURL或者timeout或者content-type等
这个时候,我们就可以创建新的实例,并且传入属于该实例的配置信息
简而言之,不同的请求需要不同的配置,我们之前使用的都是默认的axios实例,所以现在我们需要创建新的axios实例
创建axios实例去发送请求,每个axios对应不同的配置,下面是一个简单的案例:
//这里的/home配置同上,配置了跨域。
let axiosInstance = axios.create({ //这里的axiosInstance是一个axios实例,保存配置的
baseURL: '/home',
timeout: 5000
});
axiosInstance({ //使用对应的配置实例来发送网络请求,做到请求配置和实际请求的分离
url: "/api.php",
params: {
key: 'free',
appid: 0,
msg: '江西省九江市彭泽县'
}
}).then(res=>{
console.log(res);
})
根据代码规范对axios进行封装
如果我们的组件依赖某个第三方框架,我们最好在main.js入口函数中全局注册一下,例如组件使用axios,
最好不要每个组件都import导入axios模块,而是在main.js中,Vue.prototype.$axios=axios
,来全局注册
这样我们其他组件可以通过this.$axios
得到axios的对象,因为组件都是Vue构造函数的实例,
也就继承了Vue原型对象的变量和方法。
另外,需要说明的是上面这种做法其实组件还是没有和axios框架分离,因为组件内还是使用了this.$axios
,
所以一般开发中都会再对axios做一层封装,让我们的组件依赖我们自己的封装后的对象,
这样,当我们将axios换成其他框架时,我们只需要更改自己封装的内容而不需要修改组件!
按照代码规范,对axios进行封装:
//src下创建network目录,其中再创建request.js文件,该文件内容如下:
import axios from 'axios'
export function request(config) {
//创建一个axios实例
let axiosInstance = axios.create({ //这个对象感觉可以抽离,让使用者配置,但是也可以在本js文件中创建多个函数
baseURL: '/home',
timeout: 5000
});
//发送网络请求
return axiosInstance(config) //本身返回的就是promise,所以使用者调用request方法后可直接跟then函数
}
//之所以不用默认导出,因为可能你会有多个函数,每个函数有不同的axios实例去使用,如果默认导出,则只能导出一个
//使用者使用时:
import {request} from './network/request';
request({
url: "/api.php",
params: {
key: 'free',
appid: 0,
msg: '我爱你'
}
}).then(res=>{ //直接可以拿到网络请求的响应结果
console.log(res);
}).catch(res=>{ //如果网络请求出错,也可以拿到失败结果
console.log(res);
})
axios的拦截器
axios提供了拦截器,用于我们在每次发送请求之前或者得到响应后,进行相应的处理
下面是拦截器案例,也一并放到封装的axios中去了
import axios from 'axios'
export function request(config) {
//1,创建一个axios实例
let axiosInstance = axios.create({
baseURL: '/home',
timeout: 5000
});
//2,配置拦截器
//请求拦截,有两个回调函数,请求成功函数,请求失败回调函数
axiosInstance.interceptors.request.use(config=>{
console.log("发送请求之前执行");
/*
请求拦截器使用场景:
* 比如config中的一些信息不符合服务器的要求
* 比如每次发送网络请求时,都希望在界面中显示一个请求图标
* 某些网络请求需要token,必须携带一些特殊的信息,在这里可以添加到请求头中
*/
return config; //拦截器放行,不然响应就会失败(不放行代表请求失败),这个函数会在请求之前执行
},err=>{
//这里一般只要不是网络原因,都很少来到这里,这里指的是发送请求发布出去,如果是错误url,还是可以发送的出去的
console.log(err); //如果请求失败,则打印错误信息
})
//响应拦截,响应成功回调函数,响应失败回调函数
axiosInstance.interceptors.response.use(res=>{
return res; //响应成功后不放行的话,使用者的then方法将拿不到响应的数据,所以需要将响应数据放行
},error => {
console.log(error); //如果响应失败,则打印错误信息,也可能是请求不放行导致的
})
//3,发送网络请求
return axiosInstance(config) //本身返回的就是promise
}