VUE3(十六)封装axios

一: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封装的一些我的理解。

有好的建议,请在下方输入你的评论。

欢迎访问个人博客
https://guanchao.site

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容