axios
axios
参考: https://huaweicloud.csdn.net/638eaba6dacf622b8df8d10e.html
1. axios的封装
在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js中。他有很多优秀的特性,例如拦截请求和响应、取消请求、转换json、客户端防御XSRF等。所以我们的尤大大也是果断放弃了对其官方库vue-resource的维护,直接推荐我们使用axios库。如果还对axios不了解的,可以移步axios文档。
2. 安装
npm install axios
3. 使用
一般我会在项目的src目录中,新建一个request文件夹,然后在里面新建一个http.js和一个api.js文件。http.js文件用来封装我们的axios,api.js用来统一管理我们的接口。
3.1 开发环境、测试环境和生产环境
通过 [.env](../vue_项目搭建/vue项目搭建.md#8. .env)
axios.defaults.baseURL = process.env.VUE_APP_BASE_API
或者使用下面的(不推荐)
// 环境的切换
if (process.env.NODE_ENV == 'development') {
axios.defaults.baseURL = 'https://www.baidu.com';}
else if (process.env.NODE_ENV == 'debug') {
axios.defaults.baseURL = 'https://www.ceshi.com';
}
else if (process.env.NODE_ENV == 'production') {
axios.defaults.baseURL = 'https://www.production.com';
}
3.2 设置请求超时
通过axios.defaults.timeout设置默认的请求超时时间。例如超过了10s,就会告知用户当前请求超时,请刷新等。
axios.defaults.timeout = 10000;
3.3 post请求头的设置
post请求的时候,我们需要加上一个请求头,所以可以在这里进行一个默认的设置,即设置post的请求头为application/x-www-form-urlencoded;charset=UTF-8
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
3.4 其他准备
创建一个http.js h用来封装我们的axios
同时因为请求是异步的所有一般搭配 promise 使用
1. 使用 async/await
async loadData_backup () {
const { data: res } = await this.$axios.post('login', this.loginForm)
if (res.meta.status !== 200) { }
},
使用async await 处理reject
async loadData_backup () {
const { data: res } = await this.$axios.post('login', this.loginForm).catch((e) => {
console.log(e,666) // 这里返回上面的{sus: false,msg: '缺少参数'}
return {data:'这是await返回的错误信息'} // 可以重定义错误返回
})
if (res.meta.status !== 200) {
}
},
2. 使用 promise
2.1 promise
export const axiosApiFormdata = (url, params, method, headers) => {
const token = JSON.parse(localStorage.getItem('token'))
if (!headers) {
headers = { 'Content-Type': 'application/json;charset=utf-8' } // 'x-www-form-urlencoded'
}
if (token != null) {
headers.Authorization = 'Bearer ' + token
}
return new Promise((resolve, reject) => {
let param
if (method === 'post' || method === 'POST') {
param = {
method: method,
url: url,
data: params,
headers: headers
}
} else if (method === 'get' || method === 'GET') {
param = {
method: method,
url: url,
params: params,
headers: headers
}
} else { // put 请求
param = {
method: method,
url: url,
data: params,
headers: headers
}
}
axios(param)
.then((res) => {
if (res.status === 200) {
resolve(res.data)
} else {
// 接口错误提示
Message({ message: res.data.message, type: 'error' })
}
})
.catch((err) => {
console.log(`${url} 网络请求异常:${err}`)
reject(err)
})
})
}
2.2 promise.all()
let p1 = new Promise((resolve, reject) => {
getNumber()
resolve('成功了p1')
})
let p2 = new Promise((resolve, reject) => {
getName()
resolve('成功了p2')
})
let p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([p1, p2, p3]).then((result) => {
console.log(result) // ['成功了p1', '成功了p2', 'foo']
}).catch((error) => {
})
// 典型案例
async getCaseData (resolve) {
const res = await this.axiosApiFormdata('/operation/case/list', { sId: this.deptId }, 'GET')
if (res && res.code === 200 && res.data) {
this.caseList = res.data
} else {
this.caseList = []
}
console.log('getCaseData-- 数据加载完成')
resolve('success')
},
async loadData () {
console.log('loadData-- 开始数据加载')
let f1 = new Promise((resolve, reject) => {
this.getQualificationData(resolve) // 资质文件
});
let f2 = new Promise((resolve, reject) => {
this.getCp2Data(resolve) // 产品推荐
});
let f3 = new Promise((resolve, reject) => {
this.getCaseData(resolve) // 典型案例
});
// this.getQualificationData() // 资质文件
// this.getCp2Data() // 产品推荐
// this.getCaseData() // 典型案例
Promise.all([f1,f2,f3]).then((result) => {
console.log('loadData-- 加载完成')
this.initScroll();
}).catch((error) => {
})
},
submit(){
const p1=new Promise((resolve,reject)=>{
this.$refs['basic'].$refs['ruleForm'].validate(valid=>{
if(valid) resolve()
})
})
const p2=new Promise((resolve,reject)=>{
this.$refs['onshelf'].$refs['ruleForm'].validate(valid=>{
if(valid) resolve()
})
})
const p3=new Promise((resolve,reject)=>{
this.$refs['more'].$refs['ruleForm'].validate(valid=>{
if(valid) resolve()
})
})
Promise.all([p1,p2,p3]).then(()=>{
console.log('验证通过,提交表单')
})
}
// 自定义校验
validateForm(callback,errorCalback) {
const form = new Promise((resolve,reject)=>{
this.$refs['form'].validate(valid => {
console.log('form1' + valid);
if(valid) resolve()
if(!valid) reject()
})
})
const form1 = new Promise((resolve,reject)=>{
this.$refs['form1'].validate(valid => {
console.log('form1' + valid);
if(valid) resolve()
if(!valid) reject()
})
})
Promise.all([form,form1]).then(()=>{
console.log('验证通过,提交表单')
callback()
}).catch(res => {
console.log('all......');
errorCalback();
})
},
// 表单提交
submitForm () {
this.validateForm(() => {
console.log('校验成功....')
},() =>{
console.log('失败....')
})
},
3.优化
axios 请求一般搭配 nprogress 进度条使用
npm install --save nprogress
// 导入 NProgress 包对应的JS和CSS
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
4. 简单配置
/* 封装 axios */
import Vue from 'vue'
import axios from 'axios'
// 导入 NProgress 包对应的JS和CSS
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
// 配置请求的跟路径
axios.defaults.baseURL = 'http://localhost:3001/'
// 在 request 拦截器中,展示进度条 NProgress.start()
axios.interceptors.request.use(config => {
// console.log(config)
NProgress.start()
config.headers.Authorization = window.sessionStorage.getItem('token')
// 在最后必须 return config
return config
})
// 在 response 拦截器中,隐藏进度条 NProgress.done()
axios.interceptors.response.use(config => {
NProgress.done()
return config
})
Vue.prototype.$axios = axios
3.5 请求拦截
我们在发送请求前可以进行一个请求的拦截,为什么要拦截呢,我们拦截请求是用来做什么的呢?比如,有些请求是需要用户登录之后才能访问的,或者post请求的时候,我们需要序列化我们提交的数据。这时候,我们可以在请求被发送之前进行一个拦截,从而进行我们想要的操作。
axios.interceptors.request.use(config => {
// console.log(config)
NProgress.start()
config.headers.Authorization = window.sessionStorage.getItem('token')
// 在最后必须 return config
return config
})
下面的还未整理
这里说一下token,一般是在登录完成之后,将用户的token通过localStorage或者cookie存在本地,然后用户每次在进入页面的时候(即在main.js中),会首先从本地存储中读取token,如果token存在说明用户已经登陆过,则更新vuex中的token状态。然后,在每次请求接口的时候,都会在请求的header中携带token,后台人员就可以根据你携带的token来判断你的登录是否过期,如果没有携带,则说明没有登录过。这时候或许有些小伙伴会有疑问了,就是每个请求都携带token,那么要是一个页面不需要用户登录就可以访问的怎么办呢?其实,你前端的请求可以携带token,但是后台可以选择不接收啊!
3.6 响应的拦截
没有验证过
// 响应拦截器
axios.interceptors.response.use(
response => {
// 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据
// 否则的话抛出错误
if (response.status === 200) {
return Promise.resolve(response)
} else {
return Promise.reject(response)
}
},
// 服务器状态码不是2开头的的情况
// 这里可以跟你们的后台开发人员协商好统一的错误状态码
// 然后根据返回的状态码进行一些操作,例如登录过期提示,错误提示等等
// 下面列举几个常见的操作,其他需求可自行扩展
error => {
if (error.response.status) {
switch (error.response.status) {
// 401: 未登录
// 未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页操作。
case 401:
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
break
// 403 token过期
// 登录过期对用户进行提示
// 清除本地token和清空vuex中token对象
// 跳转登录页面
case 403:
Message({
message: '登录过期,请重新登录',
type: 'error'
})
// 清除token
localStorage.removeItem('token')
store.commit('loginSuccess', null)
// 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
}, 1000)
break
// 404请求不存在
case 404:
Message({
message: '网络请求不存在',
type: 'error'
})
break
// 其他错误,直接抛出错误提示
default:
Message({
message: error.response.data.message,
type: 'error'
})
}
return Promise.reject(error.response)
}
})
3.7 封装get方法和post方法
这里有个小细节说下,axios.get()方法和axios.post()在提交数据时参数的书写方式还是有区别的。区别就是,get的第二个参数是一个{},然后这个对象的params属性值是一个参数对象的。而post的第二个参数就是一个参数对象。两者略微的区别要留意哦!
1.get方法
get方法:我们通过定义一个get函数,get函数有两个参数,第一个参数表示我们要请求的url地址,第二个参数是我们要携带的请求参数。get函数返回一个promise对象,当axios其请求成功时resolve服务器返回 值,请求失败时reject错误值。最后通过export抛出get函数。
/**
* get方法,对应get请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
export function get(url, params){
return new Promise((resolve, reject) =>{
axios.get(url, {
params: params
}).then(res => {
resolve(res.data);
}).catch(err =>{
reject(err.data)
})
});
2. post方法
post方法:原理同get基本一样,但是要注意的是,post方法必须要使用对提交从参数对象进行序列化的操作,所以这里我们通过node的qs模块来序列化我们的参数。这个很重要,如果没有序列化操作,后台是拿不到你提交的数据的。这就是文章开头我们import QS from 'qs';的原因
/**
* post方法,对应post请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
export function post(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, QS.stringify(params))
.then(res => {
resolve(res.data);
})
.catch(err =>{
reject(err.data)
})
});
}
3.8 vue.config.js 设置代理解决跨域问题
module.exports = {
publicPath: './',
devServer: {
host: '127.0.0.1',
port: 8082,
https: false,
// 自动打开浏览器
open: true,
proxy: {
'/SDT_IMOA': {
target: 'http://127.0.0.1:8082/',
changeOrigin: true,
pathRewrite: {
'^/SDT_IMOA': ''
}
}
}
}
}
针对不同 的请求前缀,通过指定baseURL
this.$axios({
baseURL: 'http://220.160.202.214:18080', // 自定义自己的 请求前缀 process.env.VUE_APP_CAMERA_API,
method: 'get',
url: '/api/play/start/' + item.cameracode + '/' + item.visiblelightchannel
}).then(response => {
// console.error('======================', response.data)
if (response.data.code !== 0) {
this.$message.error(response.data.msg)
} else {
this.videoShow = true
// this.videoUrl = response.data.data.flv;
this.$refs.videoPlayer.play(response.data.data.flv)
}
})
4 路由导航守卫
src/router/index.js 下 添加
// 挂载路由导航守卫
router.beforeEach((to, from, next) => {
// to 将要访问的路径
// from 代表从哪个路径跳转而来
// next 是一个函数,表示放行
// next() 放行 next('/login') 强制跳转
if (to.path === '/login') return next()
// 获取token
const tokenStr = window.sessionStorage.getItem('token')
if (!tokenStr) return next('/login')
next()
})
其他
对于其他组件二次开发,需要公用一个文件处理
场景描述:vfrom 需要对其二次开发,其中下拉选数据需要通过本地的json文件中获取,但是由于vfrom 编译后使用在自己的项目中,所以如果该json文件在vfrom 组件中,则后期维护json数据就不方便,需要的效果是2个项目都能有各自的json文件并且可以互通
将文件存放到public目录下(2个项目都要这个目录),例如 public\resource\commonInterfaceAPi.json
vfrom中 通过axios 进行获取
axios.get('/resource/commonInterfaceAPi.json').then(({data:res}) => {
this.commonInterfaceOptions = res.data;
}).catch(error => {
console.error(error);
});
vfrom编译后,在原项目中之间调用本项目下的 commonInterfaceAPi.json 文件,这样2个项目开发互不影响,各自维护自己的json文件即可
额外补充,解构解析
// 数据格式
{
"selectApiData": {
"data": [
{
"value": "IS_AQXY",
"label": "是否签订安全协议",
}
],
"##": "用于加载常用api,针对radio,checkbox,select 给用户通过选择内置api进行数据加载,具体使用请参考:option-items-setting.vue"
}
}
通过axios 获取数据
axios.get('/resource/commonInterfaceAPi.json').then(({data: { selectApiData:res } }) => {
this.commonInterfaceOptions = res.data;
})
// 或者
axios.get('/resource/commonInterfaceAPi.json').then(({ data: { selectApiData: { data: res } } }) => { // 本质是想 selectApiData.data:res
this.commonInterfaceOptions = res;
})