1.前言
axios
多少年了依然这么流行,而且也没什么大的更新简单到极致,真是小而美
尤大的 官方库
vue-resource
都放弃维护了,推荐用axios
特点:
- 基于promise的http库
2.可运行在浏览器端和node.js中
3.拦截请求和响应
4.取消请求- 转换json
- 客户端防御XSRF等
2. vue里面 基础使用axios
1.安装 npm install axios
3x
脚手架 也可以 vue add axios
3.基础使用
新建
axios
文件main.js引入 axios文件
vue add axios
已经帮我们做了上述操作
组件使用
注意 这里不能
this.$http
版本不同
const api = "/api/v1/code"; this.axios.post(api,{test:"测试"}).then((res)=>{ console.log("验证码--",res) this.codeUrl = res.data.code })
vue.config.js配置代理
代理可以配置多个
比如你图片服务器或者 文件服务器不同也可以配置
devServer: { // port:8080, // host:"yzs.com", open: true, proxy:{ "/api":{ target:"https://xx.xx.org", ws:false, changeOrigin:true, pathRewrite:{ "^/api":"" } } } }
4. baseUrl配置
全局配置-方式1
这里主要就是 网络请求的基础配置
请求超时经常设置
let config = { baseURL: "https://www.yzs.xx.", timeout: 60 * 1000, // Timeout withCredentials: true, // Check cross-site Access-Control };
这个可以走单独的配置文件 ,不同的环境不同的地址
baseURL: process.env.baseURL || process.env.apiUrl ||""
全局配置-方式2
/ /axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || ''; // axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; // axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
5. 某些情况下,有些个别请求也需要单独配置
局部配置
this.axios.defaults.baseURL = "http://www.yzs.com" const api = "/api/v1/code"; this.axios.post(api,{test:"测试"}).then((res)=>{ console.log("验证码--",res) this.codeUrl = res.data.code })
以上配置只对 本次请求有效
6. 不同环境不同baseURL
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'; }
7. 基于element-ui的 loading配置
1.基于
vue add axios
后的axios
文件2.就是在
request
请求的时候加上loding
动画3.响应
response
的时候关闭4.也可以在响应成功里面 对响应数据做个初步处理
比如数据结构剥离一层
比如根据后端的 状态码 统一做处理
import { Loading } from 'element-ui'; const _axios = axios.create(config); var loading = null _axios.interceptors.request.use( function(config) { loading= Loading.service({ lock: true, text: 'Loading', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' }); // Do something before request is sent return config; }, function(error) { // Do something with request error return Promise.reject(error); } ); // Add a response interceptor _axios.interceptors.response.use( function(response) { loading.close() // Do something with response data return response; }, function(error) { // Do something with response error return Promise.reject(error); } );
8. api拆分
大部分道友可能习惯直接在组件内发请求
this.$http.post(this.$hosts.yzsm.YZS_HOST + '/companyRight/getAllCompanyByMap', params)
从各大项目来看比如各大框架的
admin
模板,api也都是拆分的但是从项目架构角度出发,这样写非常不方便查阅和管理
所以单独拆分出来文件,这个想法其实和
store
的拆分一样
步骤
- src/api 目录 或者assets/api目录 这个在哪倒是无伤大雅
- 新建文件,一般按功能分,不按界面
比如用户信息相关user.js
比如登录注册相关login.js
比如城市相关city.js
比如店铺相关shop.js
比如权限相关right.js
- 也不必非得太细
注释必不可少
get
和post
都一样 参数根据需要传递
//获取权限列表 export const getAllRights = (params)=>{ return axios.get("/api/companyRight/getAllCompanyByMap", {params}) } // 更新权限 export const updateRight = ()=>{ return axios.get("/api/companyRight/updateCompanyRight") } // 更新管理员权限 export const updateAdmin = ()=>{ return axios.get("/api/companyRight/updateCompanyAdmin") }
9. api使用
1. 引入
因为接口一般都比较多选用 别名的方式引入
import * as api from "@/api/rights.js";
如果比较少也可以 直接引入
import {updateAdmin ,updateRight ,getAllRights } from "@/api/rights.js";
根据自己的情况选用一种就行
2. 使用
别名引入 直接 别名.属性 调用
直接引入就直接用, 但是注意不要和 组件内方法重名
所有还是建议使用别名的方式
methods: { getRightsBtn() { var params = { }; api.getAllRights (params).then((res) => { if(res.data.bean !== "2000"){ return this.$message.error(res.data.returnMessage) } this.listData = res.data.bean.list console.log("Res:",res) }) .catch((err) => { console.log("失败", err); }); }, },
10. 请求拦截
why
为啥要拦截呢
有些请求是需要用户登录之后才能访问的,或者post请求的时候,我们需要序列化我们提交的数据。这时候,我们可以在请求被发送之前进行一个拦截,从而进行我们想要的操作。
- 需要导入
vuex
,因为这里面管理着我们需要的状态,比如,用户是否登录,用户会员级别
_axios.interceptors.request.use( config => { //在发送请求之前做些什么 // 每次发送请求之前判断vuex中是否存在token // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况 // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 const token = store.state.token; token && (config.headers.Authorization = token); return config; }, function(error) { // 对请求错误做些什么 // Do something with request error return Promise.reject(error); } );
这里用到的是 vuex add axios 添加后自动生成的
11. token 简要
1.一般是在登录完成之后,将用户的token通过localStorage或者cookie存在本地
2.然后用户每次在进入页面的时候(即在main.js中),会首先从本地存储中读取token
3.如果token存在说明用户已经登陆过,则更新vuex中的token状态。
4.然后,在每次请求接口的时候,都会在请求的header中携带token,
5.后台人员就可以根据你携带的token来判断你的登录是否过期,如果没有携带,则说明没有登录过。
6.每个请求都携带token,那么要是一个页面不需要用户登录就可以访问的怎么办呢?前端的请求可以携带token,但是后台可以选择不接收啊!
12. token 取消
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // executor 函数接收一个 cancel 函数作为参数 cancel = c; }) }); // cancel the request cancel();
13. 扩展 -响应拦截-配置
就是服务器返回给我们的数据,我们在拿到之前可以对他进行一些处理
如果后台返回的状态码是200,则正常返回数据,
否则的根据错误的状态码类型进行一些我们需要的错误,其实这里主要就是进行了错误的统一处理和没登录或登录过期后调整登录页的一个操作。
_axios.interceptors.response.use( response => { // 对响应数据做点什么 // 如果返回的状态码为200,说明接口请求成功,可以正常拿到数据 // 否则的话抛出错误 res => res.status === 200 ? Promise.resolve(res) : Promise.reject(res), }, // 服务器状态码不是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: Toast({ message: '登录过期,请重新登录', duration: 1000, forbidClick: true }); // 清除token localStorage.removeItem('token'); store.commit('loginSuccess', null); // 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面 setTimeout(() => { router.replace({ path: '/login', query: { redirect: router.currentRoute.fullPath } }); }, 1000); break; // 404请求不存在 case 404: Toast({ message: '网络请求不存在', duration: 1500, forbidClick: true }); break; // 其他错误,直接抛出错误提示 default: Toast({ message: error.response.data.message, duration: 1500, forbidClick: true }); } return Promise.reject(error.response); } } });
window.navigator.onLine 可以判断是否在线