二,vuex+axios请求封装

393 阅读3分钟

一,vuex

1.安装

npm i vuex@next

2.在src文件夹下面创建store文件夹(如图所示)

Snipaste_2022-08-23_13-47-18.png

  1. 我们在index.ts中开始写我们的vuex
// 创建状态管理
import { createStore } from "vuex";

// 根节点状态属性
interface IRootState {
    // 这里可以做一下属性记录
}

// 动态引入其他子模块
let modulesFiles = import.meta.globEager('./modules/*.ts')
let fileListFun = (list:any) => {
  return Object.keys(list).reduce((moduless:any, modulePath:any) => {
    const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1').split('/')[ modulePath.replace(/^\.\/(.*)\.\w+$/, '$1').split('/').length - 1]
    const value = list[modulePath]
    moduless[moduleName] = value.default
    return moduless
  }, {})
}
const modules = fileListFun(modulesFiles)

const store = createStore<IRootState>({
    state:{},
    mutations:{},
    actions:{},
    modules:{
        ...modules
    }
})
export default store
  1. 在main.ts中引入
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import store from './store/index'
const app = createApp(App)

app.use(store).use(ElementPlus).mount('#app')

3.我们以登录为例写一个模块(如图所示)创建login.ts

Snipaste_2022-08-23_14-00-27.png

login.ts

import api from '../../api/login' //引入的登录接口api
import { Module } from 'vuex' //子模块必须添加
//根节点状态属性约束
interface IRootState {

}
//state属性约束
interface ILoginState {
    userInfo:{},
    token:string
}
const state:any = {}
const mutations:any = {}
const actions:any = {}

//动态使用调用
for (const key in api) {
    if(api[key].mutationsName) {
        state[api[key].stateName] =  api[key].stateDefaultName || ''
        mutations[api[key].mutationsName] = (state:any, data:any) => {
            if(api[key].getStateName) {
                let getStateNameList = api[key].getStateName.split('.')
                let i = 0
                let stateValue = data
                while(i< getStateNameList.length) {
                    stateValue = stateValue[getStateNameList[i]]
                    i++
                }
                state[api[key].stateName] = stateValue
            } else {
                state[api[key].stateName] =  data
            }
        }
    }
    actions[key] = async ({commit}:any, params = {}) => {
        if(api[key].mutationsName) {
            let res = await api[key].requestFun(params)
            commit(api[key].mutationsName, res)
            return res
        } else {
            let res = await api[key].requestFun(params)
            return res
        }
    }
}
const loginModule:Module<ILoginState,IRootState> = {
    namespaced:true,
    state,
    mutations,
    actions
}

export default loginModule

二, axios封装请求接口

1.在src文件夹的utils文件夹中新建baseUrl.ts文件 baseUrl.ts:

export const getBaseUrl = () =>{
    let baseURL = ''
    if(import.meta.env.Env === 'development'){
        baseURL = 'https://demo.fat.net' //测试请求地址
    }  else {
      baseURL = 'https://demo.net' //线上请求地址
    }
     
      return baseURL
}

2.安装axios

    npm i axios

1.在src文件夹的utils文件夹中新建request.ts文件开始封装请求

import Axios from "axios";
import { getBaseUrl } from "./baseUrl";
import { useRouter, useRoute } from 'vue-router';
import { errorCodeType } from "./error-code-type"; //请求错误类型
import { ElMessage, ElLoading } from 'element-plus'

const BASE_URL = getBaseUrl()
const TIME_OUT: any = ''
const router = useRouter(), route = useRoute()
const instance = Axios.create({
    baseURL: BASE_URL,
    timeout: TIME_OUT,
    withCredentials: false,
    headers: {
        'Content-Type': 'application/json;charset=utf-8',
        // token:localStorage.getItem('token')
    }
})

export function sendPost(url: string, data = {}, methods = "post") {
     return new Promise((resolve, reject) => {
        instance({
            url: url,
            method: methods,
            data
        }).then((res: any) => {
            if(res.code == 0) {
                resolve(res.data)
            } else if (res.code == 413) {
                ElMessage.error(res.message)
                localStorage.removeItem('token')
                localStorage.removeItem('name')
                localStorage.removeItem('avatar_url')
                //localStorage.removeItem('breadCrumbList')
                //localStorage.removeItem('institution')
                router.replace('/login')
            } else {
                ElMessage.error(res.message)
                reject(res.message)
            }
        }).catch(err => {
            reject(err);
          });
    })
}

let loading: any
// 正在请求的数量
let requestCount: number = 0
// 显示loading
const showLoading = () => {
    if (requestCount === 0 && !loading) {
        loading = ElLoading.service({
            text: '加载中,请稍后...',
            background: 'rgba(0,0,0,.7)',
            spinner: 'el-icon-loading'
        })
    }
    requestCount++
}
// 隐藏loading
const hideLoading = () => {
    requestCount--
    if (requestCount == 0) {
        loading.close()
    }
}

interface configRule {
    [key: string]: any
}

instance.interceptors.request.use((config: configRule) => {
    showLoading()
    //是否需要设置 token
    config.headers['token'] = localStorage.getItem('token')
    if (config.method === 'get' && config.params) {
        let url = config.url + '?'
        for (const propName of Object.keys(config.params)) {
            const value = config.params[propName]
            var part = encodeURIComponent(propName) + '='
            if (value !== null && typeof (value) !== 'undefined') {
                if (typeof value === 'object') {
                    for (const key of Object.keys(value)) {
                        let params = propName + '[' + key + ']'
                        var subPat = encodeURIComponent(value[key]) + '&'
                    }
                } else {
                    url += part + encodeURIComponent(value) + '&'
                }
            }
        }
        url = url.slice(0, -1)
        config.params = {}
        config.url = url
    }
    return config
}), (error: any) => {
    return Promise.reject(error)
}

instance.interceptors.response.use((res: any) => {
    hideLoading()
    const code = res.data['code'] || 200
    const msg = errorCodeType(code) || res.data['msg']
    if (code == 200) {
        return Promise.resolve(res.data)
    } else {
        ElMessage.error(msg)
        return Promise.reject(res.data)
    }
    // const res = response.data
}), (error: any) => {
    hideLoading()
    let { message } = error
    if (message == 'Network Error') {
        message = '后端接口连接异常'
    } else if (message.includes('timeout')) {
        message = '系统接口请求超时'
    } else if (message.includes('Request failed with status code')) {
        message = '系统接口' + message.substr(message.length - 3) + '异常'
    }
    ElMessage.error({
        message: message,
        duration: 5 * 1000
    })
    return Promise.reject(error)
}

error-code-type.ts


export const errorCodeType = function (code: number) {
    let msg: string = ''
    switch (code) {
        case 400:
            msg = '请求错误'
            break
        case 401:
            msg = '未授权,请登录'
            break

        case 403:
            msg= '拒绝访问'
            break

        case 404:
            msg = `请求地址出错: `
            break

        case 408:
            msg = '请求超时'
            break

        case 500:
            msg = '服务器开个小差,请稍后再试'
            break

        case 501:
            msg = '服务器开个小差,请稍后再试'
            break

        case 502:
            msg = '服务器开个小差,请稍后再试'
            break

        case 503:
            msg = '服务器开个小差,请稍后再试'
            break

        case 504:
            msg = '服务器开个小差,请稍后再试'
            break

        case 505:
            msg = 'HTTP版本不受支持'
            break

        default:
            msg = '请求失败,请稍后再试'
    }
    return msg
}

基础封装已经完毕,我们就可以去封装请求了

3.接口封装:在src文件夹下面创建api文件夹,以login为例,我们在api文件夹下面创建一个login.ts

login.ts:

import { sendPost } from '../utils/request'

interface apiRule{ //简约版
	[key:string]:any
}

interface apiRule{ //详细版
	[key:string]:{
        url:string,
        mutationsName?:string,
        stateName?:string
    }
}

const api:apiRule = {
    'login': {
	url:'/login/login/index'
    },
    'demo': {//如果接口内容需要进行数组修改mutations,或者state时
        url: '/demo/demo',
	mutationsName: 'mutationsDemo',
	stateName: 'stateDemo',
    },
}

const requestFunObj:any = {}

for (const key in api) {
    requestFunObj[key]  = {
        ...api[key],
        requestFun:(params:any, method='post') => {
            return sendPost(api[key].url, params, method)
        }
    } 
	
}

export default requestFunObj