一:axios 基础
从浏览器中创建XMLHttpRequest
从node.js发出http请求
支持Promise API
拦截请求和响应
转换请求和响应数据
取消请求
自动转换JSON数据
客户端支持防止CSRF/XSRF
二:axios封装
关于Axios的封装这部分涉及到与后端的一些约定。
什么约定呢?就是在我们请求接口的时候,后端会返回给我们一个code。
在我开发的时候,一般约定
code:-200为登录失效状态 code:-100为接口发生错误状态。(为了避免服务器端接口报错导致前端无法运行的情况发生,我的习惯通常是在后端的接口加上try{}catch(){} 来避免接口报错) code > 0 接口返回成功值 code < 0 接口返回失败值
这样,我们就可以在封装axios的时候提前通过请求返回值code做一些预处理。
1:引入axios
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
2:设置请求超时
通过axios.defaults.timeout设置默认的请求超时时间。例如超过了10s,就会告知用户当前请求超时,请刷新等。
// 超时时间(ms) axios.defaults.timeout = 2000 * 1000;
3:post请求头的设置
post请求的时候,我们需要加上一个请求头,所以可以在这里进行一个默认的设置,即设置post的请求头为application/x-www-form-urlencoded;charset=UTF-8
// axios 请求头 axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest' axios.defaults.headers['token'] = localStorage.getItem('token') || '' axios.defaults.headers.post['Content-Type'] = 'application/json'
4:请求拦截
我们在发送请求前可以进行一个请求的拦截,为什么要拦截呢,
我们拦截请求是用来做什么的呢?
比如,有些请求是需要用户登录之后才能访问的,或者post请求的时候,我们需要序列化我们提交的数据。这时候,我们可以在请求被发送之前进行一个拦截,从而进行我们想要的操作。
// 请求拦截 axios.interceptors.request.use( (config: AxiosRequestConfig) => { // 可在这里做一些数据的校验。 // session的校验等。 return config }, (error: AxiosError) => { return error } )
5:响应拦截
响应拦截器很好理解,就是我在最开始的时候说的那部分和后端的约定,约定好几个固定的状态码,按需执行对应的操作就好。
当然,服务器返回给我们的数据,我们在拿到之前可以对他进行一些处理。
例如上面的思想:如果后台返回的状态码是200,则正常返回数据,否则的根据错误的状态码类型进行一些我们需要的错误。
其实这里主要就是进行了错误的统一处理和没登录或登录过期后调整登录页的一个操作。
// 响应拦截 axios.interceptors.response.use((result: AxiosResponse) => { // =========================================================== // 返回方式一 /*console.log(result); if (result.status === 200) { if (result.data && result.data.code > 0) { return Promise.resolve(result); } else { alert(result.data.msg || "操作失败"); return Promise.reject(result); } } else { alert("网络异常"); return Promise.reject(result); }//*/ // ========================================================== // 返回方式二 // 返回数据前做了什么 // console.log(result); if (result.data.code < -100) { if (result.data.msg) { // 调用自定义alert utils.alert(result.data.msg, function () { window.location.assign('/pc/index'); }); } return Promise.reject(result.data.data) } return result; }, (err: AxiosError) => { utils.alertLoadExec(false); // 返回数据前做了什么 return Promise.reject(err) })
6:封装get,put,post请求
Request.ts
import axios from "axios"; import qs from "qs"; /** * 封装请求方式 */ const request = { /** * @name: 封装axios get方法 * @desc: 描述 * @author: camellia * @email: guanchao_gc@qq.com * @date: 2020-12-21 * @param url 请求连接 * @param params 请求参数 * @param callback 回调方法 */ get(url: string, params: any, callback: any) { return new Promise((resolve, reject) => { axios .get(url, { params: params }) .then(res => { callback ? resolve(callback(res.data)) : resolve(res.data); }) .catch(err => { reject(err); }); }); }, /** * @name: 封装axios post方法 * @desc: 描述 * @author: camellia * @email: guanchao_gc@qq.com * @date: 2020-12-21 * @param url 请求连接 * @param params 请求参数 * @param callback 回调方法 */ post(url: string, params: any, callback: any) { return new Promise((resolve, reject) => { axios .post(url, qs.stringify(params)) .then(res => { callback ? resolve(callback(res.data)) : resolve(res.data); }) .catch(err => { reject(err); }); }); }, /** * @name: put请求封装 * @author: camellia * @email: guanchao_gc@qq.com * @date: 2021-03-01 * @param url 请求连接 * @param params 请求参数 * @param callback 回调方法 */ put(url: string, params: any, callback: any) { return new Promise((resolve, reject) => { axios .put(url, params) .then(res => { callback ? resolve(callback(res.data)) : resolve(res.data); }, err => { reject(err) }) }) }, /** * @name: 请求失败后的错误统一处理 * @author: camellia * @email: guanchao_gc@qq.com * @date: 2021-03-08 * @param {Number} status 请求失败的状态码 */ errorHandle(status:any, other:any) { // 状态码判断 switch (status) { // 401: 未登录状态,跳转登录页 case 401: // toLogin(); break; // 403 token过期 // 清除token并跳转登录页 case 403: // tip('登录过期,请重新登录'); // localStorage.removeItem('token'); // store.commit('loginSuccess', null); setTimeout(() => { // toLogin(); }, 1000); break; // 404请求不存在 case 404: // tip('请求的资源不存在'); break; default: console.log(other); } } } export default request;
7:axios封装完整代码
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'; // 公共状态文件 import { common } from "/@/hooks/common.ts"; // 引入公共函数js文件 import utils from "/@/assets/js/public/function"; // 默认请求连接 // axios.defaults.baseURL = "http://xxxx.xxx.xxxx/index.php"; // 超时时间(ms) axios.defaults.timeout = 2000 * 1000; // axios请求开启cookie,支持跨域请求携带cookie axios.defaults.withCredentials = true; // axios 请求头 axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest' axios.defaults.headers['token'] = localStorage.getItem('token') || '' axios.defaults.headers.post['Content-Type'] = 'application/json' // 请求拦截 axios.interceptors.request.use( (config: AxiosRequestConfig) => { // 可在这里做一些数据的校验。 // session的校验等。 return config }, (error: AxiosError) => { return error } ) // 响应拦截 axios.interceptors.response.use((result: AxiosResponse) => { // =========================================================== // 返回方式一 /*console.log(result); if (result.status === 200) { if (result.data && result.data.code > 0) { return Promise.resolve(result); } else { alert(result.data.msg || "操作失败"); return Promise.reject(result); } } else { alert("网络异常"); return Promise.reject(result); }//*/ // ========================================================== // 返回方式二 // 返回数据前做了什么 // console.log(result); if (result.data.code < -100) { if (result.data.msg) { // 调用自定义alert utils.alert(result.data.msg, function () { window.location.assign('/pc/index'); }); } return Promise.reject(result.data.data) } return result; }, (err: AxiosError) => { utils.alertLoadExec(false); // 返回数据前做了什么 return Promise.reject(err) }) export default axios
8:api统一管理
统一的api管理,我是将每个单页的请求放到一个对应的文件中,这样使用以及管理更加灵活。而且也不会出现多人协作开发的时候,出现重名的情况。
我这里用一个来做例子:
Api文件:articleList.ts
// 引入公共js文件 import request from "/@/hooks/request"; /** * @name:根据分类获取文章列表 * @author: camellia * @email: guanchao_gc@qq.com * @date: 2021-03-01 */ export const getArticleListByCategory = (data: any) => request.get("/index.php/article/getArticleListByCategory", data, '');
页面文件:articleList.ts
import { PropType, ref, watch, reactive, toRefs, provide, inject, } from "vue"; // 引入axios钩子 import axios from "/@/hooks/axios.ts"; // 引入路由 import { useRouter, useRoute } from "vue-router"; import HelloWorld from "/@/components/HelloWorld.vue"; import Footer from "/@/components/pc/Footer.vue"; import Header from "/@/components/pc/Header.vue"; import Menu from "/@/components/pc/Menu.vue"; import load from "/@/components/pc/loading.vue"; import TopIM from "/@/components/pc/TopIM.vue"; import Drawer from "/@/components/pc/Drawer.vue"; import Pagination from "/@/components/pc/Pagination.vue"; // 引入公共js文件 import utils from "/@/assets/js/public/function"; // api 接口文件 import { getArticleListByCategory } from "/@/api/pc/articleList.ts"; // 公共状态文件 import { common } from "/@/hooks/common.ts"; export default { name: "articleList", components: { HelloWorld, Footer, Header, Menu, load, TopIM, Drawer, Pagination }, // VUE3 语法 第一个执行的钩子函数 // setup官方文档 // https://www.vue3js.cn/docs/zh/guide/composition-api-setup.html#参数 setup(props: any, content: any) { // 实例化路由 const router = useRouter(); const route = useRoute(); /** * @name: 声明data * @author: camellia * @email: guanchao_gc@qq.com * @date: 2021-01-18 */ const data = reactive({ showRef: 0, // loading 是否显示 loading: true, // 文章列表 articleList: [], // 数据页数 articlePage: 0, // 当前页 currentPage: route.query.page ? route.query.page : 1, // 分页显示页码数 dataNum: 7, // 分类id cate_id: route.query.cate_id ? route.query.cate_id : '', // 分类名称 cat_name:'', // 分类列表 categoryList:'', // 子分类 cate_id_son: route.query.cate_id_son ? route.query.cate_id_son : '', // 标签id label_id: route.query.label_id ? route.query.label_id : '', // 搜索字符串 search: route.query.search ? route.query.search : '', }); /** * @name: 监听搜索值变化 * @author: camellia * @email: guanchao_gc@qq.com * @date: 2020-12-21 */ watch( () => common.search, () => { data.search = common.search; data.currentPage = 1; data.cate_id = ''; data.cate_id_son = ''; data.label_id = ''; getData(); } ); /** * @name: loading显示时间 * @author: camellia * @email: guanchao_gc@qq.com * @date: 2020-12-21 */ // utils.sleep(1000).then(() => { // // 这里写sleep之后需要去做的事情 // data.loading = false; // common.loading = data.loading; // }); // =================================================================== /** * @name: 右上角菜单 * @author: camellia * @email: guanchao_gc@qq.com * @date: 2021-01-15 * @param: param number menu是否显示 * @param: cate number 显示文章分类id */ const closeMenu = (param: number,cate:string='') => { // param就是子组件传过来的值 data.showRef = param; if(cate != '') { data.cate_id = cate; data.currentPage = 1; data.cate_id_son = ''; data.label_id = ''; getData(); } } const showMenuByChild = (param: number) => { data.showRef = param; // this.$refs.menuShowObj.getSrcList(this.showRef); } // =================================================================== /** * @name: 获取初始数据 * @author: camellia * @email: guanchao_gc@qq.com * @date: 2021-01-15 */ const getData = () => { // 文档 :http://www.axios-js.com/zh-cn/docs/ let info = { page: data.currentPage, cate_id: data.cate_id, cate_id_son: data.cate_id_son, search:data.search, label_id:data.label_id }; let param = utils.createRouterParam(info); data.loading = true; try { getArticleListByCategory(param).then(function (response: any) { data.cat_name = response.cateName; data.categoryList = response.cateList; data.articlePage = response.articlePage; data.articleList = response.articleShow; data.loading = false; utils.goToScrollTop(); }); } catch (error) { utils.alertMsg(2000, '系统错误'); } /*axios.get('/index.php/article/getArticleListByCategory', { params: param }) .then(function (response: any) { data.cat_name = response.data.cateName; data.categoryList = response.data.cateList; data.articlePage = response.data.articlePage; data.articleList = response.data.articleShow; data.loading = false; utils.goToScrollTop(); }) .catch(function (error: any) { });//*/ } // ============================================================================= // 初始调用 getData(); /** * @name: 将data绑定值dataRef * @author: camellia * @email: guanchao_gc@qq.com * @date: 2021-01-10 */ const dataRef = toRefs(data); return { showMenuByChild, // showMenu , closeMenu, ...dataRef } } };
以上大概就是对axios封装的一些我的理解。