Vue项目vue技术文档VUE

三、撸vue/cli 3.+ 的正确姿势(axios插件、实现

2019-04-26  本文已影响572人  Baby_ed6e

1、为什么axios插件的封装

axios插件一般需要再次封装,比如设置header头,比如要设置统一的错误处理机制,总之包装一会让你开发更加便捷

2、新建一个request.js 并设置axios插件的config

import axios from "axios";
import qs from "qs";  //  一个序列化的插件,直接安装该插件即可(npm i qs)
import { message } from "ant-design-vue";   // 采用这ant-design-vue作为ui库,这里只是一个message提示工具,如果采用element ui 或者其他ui,对应使用提示组件就行了。
import store from "@/store/index";  // vuex,引入全局的状态
import baseURL from "./baseURL";  // 如第二章节讲的,根据不同环境提供了不同的打包地址

// 创建一个axios实例
const service = axios.create({
    baseURL: baseURL,
    timeout: 30000,
    //`transformRequest`选项允许我们在请求发送到服务器之前对请求的数据做出一些改动,其作用是让参数(data)序列化
    //该选项只适用于以下请求方式:`put/post/patch`
    //数组里面的最后一个函数必须返回一个字符串、-一个`ArrayBuffer`或者`Stream`
    // qs.stringify()将对象 序列化成URL的形式,以&进行拼接(需要qs在此转化为formdata,和api开发人员规定)
    transformRequest: [
        function(data) {
            return qs.stringify(data);
        }
    ],
    //`paramsSerializer`是一个可选的函数,其作用是让参数(params)序列化
    // 该选项只适用于以下请求方式:`get / delete ` 
    paramsSerializer: function(params) {
        return qs.stringify(params);
    },
    //`transformResponse`选项允许我们在数据传送到`then/catch`方法之前对数据进行改动
    // 有些时候后台返回的response中真实数据在该对象下某个字段,例如 response.data,又或者是response本身,这里允许返回真实可利用的数据。
    transformResponse: [
        function(data) {
            return data;
        }
    ],
    // 自定义loading参数,可在调用调用时候覆盖,也可以在拦截器里判断状态等。
    // 这样我们可以轻松的定义是否需要在请求之前使用loading组件。
    loading: true,
    // 是否跨域,当然你也可以使用代理proxy 、nginx、
    // 但是一般公司都不会每次都设置代理,而是使用固定的开发模式,例如直接做一个跨域的设置,一劳永逸
    // 这里设置该属性还需要api开发者支持,设置后台的php程序里的header
    withCredentials: true,
    // 请求头,因为我们要使用formdata形式,所以设置如下
    "Content-Type": "application/x-www-form-urlencoded"
    // 如果采用json传递给后台数据,格式如下
    // "Content-Type": "application/json;charset=utf-8"
});

3、设置拦截

import axios from "axios";
import qs from "qs"; 
import { message } from "ant-design-vue"; 
import store from "@/store/index"; 
import baseURL from "./baseURL"; 

const service = axios.create({
    baseURL: baseURL,
    timeout: 30000,
    transformRequest: [
        function(data) {
            return qs.stringify(data);
        }
    ],
    paramsSerializer: function(params) {
        return qs.stringify(params);
    },
    transformResponse: [
        function(data) {
            return data;
        }
    ],
    loading: true,
    withCredentials: true,
    "Content-Type": "application/x-www-form-urlencoded"
});

// 请求前
service.interceptors.request.use(
    config => {
        // 判断该次请求,是否需要loading,这里修改了一个全局状态。
        // 这个loading是整个app层面的,暂且知道是可以调起整个页面loading的玩意儿
        if (config.loading) {
            store.dispatch("setLoading", true);
        }
        // 利用了一个函数,每次请求设置一个半小时后的时间放在,store和本地缓存里
        // 再请求之前,如果该次请求距离上次请求超过半个小时,就清空store和本地缓存里的token
        // 这样做的功能是,如果半小时后没操作请求,自动退出,一种安全机制,后续会详细解说,此处忽略即可,不影响封装的实现。
        store.dispatch("CheckTokenExpiredTime"); 
        // 设置header里的token
        if (store.getters.token) {
            config.headers["Authorization"] = "Bearer " + store.getters.token;
        }
        return config;
    },
    error => {
        console.log(error);
        Promise.reject(error);
    }
);

// 请求后
service.interceptors.response.use(
    response => {
        // 服务器请求成功
        // 修改loading状态
        if (response.config.loading) {
            store.dispatch("setLoading", false);
        }
        // 我们接口状态是在服务器请求200的情况下,再次返回自定义的状态码
        // 服务器请求成功,也有可能会告诉少个参数等报错,此处允许我们自定义哪些报错抛出,哪些需要重新登录等(401,403)。
        let data = JSON.parse(response.data);
        if (data.ret_code === 200) {
            return data.result;
        } else {
            if (data.ret_code === 403 || data.ret_code === 401) {
                store.dispatch("setLoginDialog", true); // 一个弹出登录框的玩意儿
            }
            message.error(data.ret_msg); // 报错信息
            return Promise.reject(data);
        }
    },
    errorReponse => {
        // 服务器请求错误
        // 为了保持统一的错误处理,修改了浏览器报错的格式,与接口返回的失败格式一致
        let response = errorReponse.response;
        if (response.config.loading) {
            store.dispatch("setLoading", false);
        }
        let errorJSON = {
            ret_code: response.status,
            ret_msg: errorReponse.message,
            status: "Request Error",
            result: errorReponse.response
        };
        if (errorJSON.ret_code === 403 || errorJSON.ret_code === 401) {
            store.dispatch("setLoginDialog", true);
        }
        message.error(errorJSON.ret_msg);
        return Promise.reject(errorJSON);
    }
);

3、如何使用

在api目录下建立一个news.js文件,以下是增删改查的用法,任何一个模块的js都可以用如下写法

import request from "@/utils/request";

export default {
    getList(params = {}) {
        return request({
            url: "admin/exams",
            method: "get",
            loading: false,
            params: params  // get请求,注意这里是params
        });
    },
    detail(params = {}) {
        return request({
            url:  "admin/exams/" + params.id,
            method: "get",
            params: {}
        });
    },
    add(params = {}) {
        return request({
            url:  "admin/exams",
            method: "post",
            data: params // 这里是data
        });
    },
    update(params = {}) {
        return request({
            url:  "admin/exams/" + params.exam_id,
            method: "patch",
            data: params
        });
    },
    delete(params = {}) {
        return request({
            url: "admin/exams/" + params.id,
            method: "delete",
            data: {}
        });
    }
};

在.vue文件中如何使用api

import api from "@/api/articles";
export default {
    data() {
        return {
            id: this.$route.params.id,
            info: {}
        };
    },
    created() {
        this.getDetail();
    },
    methods: {
        getDetail() {
            api.detail({
                id: this.id
            }).then(res => {
                this.info = res;
            }).catch(err => {
                console.log(err);
            });
        }
    }
};

到这里就大功告成了,把所有的请求提取到api目录下的分模块的js文件中,通俗易懂,能够让界面清晰明了

上一篇 下一篇

猜你喜欢

热点阅读