vue3、ts如何封装 axios,使用mock.js

简介: vue3、ts如何封装 axios,使用mock.js

一、axios 的依赖安装与处理

 1. 依赖安装

       使用异步网络请求肯定离不开loading、message 等提示,今天我们配合 elementPlus 一起使用;

// 安装axios 
npm install axios --save
// 安装 elementPlus
npm install element-plus --save

 2. 全局 axios 封装

src 目录下 utils 目录下,新建 request.ts,因为使用的是TS,需要提前定义数据格式:


定义请求数据返回的格式,需要提前确认好

定义 axios 基础配置信息

请求拦截器:所有请求最先到达的地方,我们可以在此自定义请求头信息(比如:token、多语言等等)

响应拦截器:返回数据最先到达的地方,我们可以在此处理异常信息(比如:code为401重定向至登录、code为500提示错误信息)

import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
import { ElMessage, ElLoading, ElMessageBox } from "element-plus";
// response interface { code, msg, success }
// 不含 data
interface Result {
    code: number,
    success: boolean,
    msg: string
}
// request interface,包含 data
interface ResultData<T = any> extends Result {
    data?: T
}
enum RequestEnums {
    TIMEOUT = 10000, // 请求超时 request timeout
    FAIL = 500, // 服务器异常 server error
    LOGINTIMEOUT = 401, // 登录超时 login timeout
    SUCCESS = 200, // 请求成功 request successfully
}
// axios 基础配置
const config = {
    // 默认地址,可以使用 process Node内置的,项目根目录下新建 .env.development
    baseURL: process.env.VUE_APP_BASE_API as string,
    timeout: RequestEnums.TIMEOUT as number, // 请求超时时间
    withCredentials: true, // 跨越的时候允许携带凭证
}
class Request {
    service: AxiosInstance;
    constructor(config: AxiosRequestConfig) {
        // 实例化 serice
        this.service = axios.create(config);
        /**
         * 请求拦截器
         * request -> { 请求拦截器 } -> server
         */
        this.service.interceptors.request.use(
            (config: AxiosRequestConfig) => {
                const token = localStorage.getItem('token') ?? '';
                return {
                    ...config,
                    headers: {
                        'customToken': "customBearer " + token
                    }
                }
            },
            (error: AxiosError) => {
                // 请求报错
                Promise.reject(error)
            }
        );
        /**
         * 响应拦截器
         * response -> { 响应拦截器 } -> client
         */
        this.service.interceptors.response.use(
            (response: AxiosResponse) => {
                const { data, config } = response;
                if (data.code === RequestEnums.LOGINTIMEOUT) {
                    // 表示登录过期,需要重定向至登录页面
                    ElMessageBox.alert("Session expired", "System info", {
                        confirmButtonText: 'Relogin',
                        type: 'warning'
                    }).then(() => {
                        // 或者调用 logout 方法去处理
                        localStorage.setItem('token', '');
                        location.href = '/'
                    })
                }
                if (data.code && data.code !== RequestEnums.SUCCESS) {
                    ElMessage.error(data);
                    return Promise.reject(data);
                }
                return data
            },
            (error: AxiosError) => {
                const { response } = error;
                if (response) {
                    this.handleCode(response.status);
                }
                if (!window.navigator.onLine) {
                    ElMessage.error("网络连接失败,请检查网络");
                    // 可以重定向至404页面
                }
            }
        )
    }
    public handleCode = (code: number): void => {
        switch (code) {
            case 401:
                ElMessage.error("登陆失败,请重新登录");
                break;
            case 500:
                ElMessage.error("请求异常,请联系管理员");
                break;
            default:
                ElMessage.error('请求失败');
                break;
        }
    }
    // 通用方法封装
    get<T>(url: string, params?: object): Promise<ResultData<T>> {
        return this.service.get(url, { params });
    }
    post<T>(url: string, params?: object): Promise<ResultData<T>> {
        return this.service.post(url, params);
    }
    put<T>(url: string, params?: object): Promise<ResultData<T>> {
        return this.service.put(url, params);
    }
    delete<T>(url: string, params?: object): Promise<ResultData<T>> {
        return this.service.delete(url, { params });
    }
}
export default new Request(config)

 3. 实际使用

src 目录下新增 api/index.ts

  1. 定义请求的参数类型
  2. 定义响应想具体参数类型

这里我们使用到ts 中的 namespace ,实际开发中我们很多 api 可能会出现相同名字不同含义,所以我们使用 namespace 进行定义

import request from "@/utils/request";
namespace User {
    // login
    export interface LoginForm {
        userName: string,
        password: string
    }
}
export namespace System {
    export interface Info {
        path: string,
        routeName: string
    }
    export interface ResponseItem {
        code: number,
        items: Array<Sidebar>,
        success: boolean
    }
    export interface Sidebar {
        id: number,
        hashId: string | number,
        title: string,
        routeName: string,
        children: Array<SidebarItem>,
    }
    export interface SidebarItem {
        id: number,
        parentId: number,
        hashId: string | number,
        title: string,
    }
}
export const info = (params: System.Info) => {
    // response 
    if (!params || !params.path) throw new Error('Params and params in path can not empty!')
    // 这里因为是全局的一个info,根据路由地址去请求侧边栏,所需不用把地址写死
    return request.post<System.Sidebar>(params.path, { routeName: params.routeName })
}

   Vue 文件中调用

<script lang="ts" setup name="Sidebar">
import { ref, reactive, onBeforeMount } from "vue"
import { info } from "@/api"
import { useRoute } from "vue-router"
const route = useRoute();
let loading = ref<boolean>(false);
let sidebar = ref<any>({});
const _fetch = async (): Promise<void> => {
    const routeName = route.name as string;
    const path = '/' + routeName.replace(routeName[0], routeName[0].toLocaleLowerCase()) + 'Info'
    try {
        loading.value = true;
        const res = await info({ path, routeName });
        if (!res || !res.data) return;
        sidebar.value = res.data;
    } finally {
        loading.value = false
    }
}
onBeforeMount(() => {
    _fetch();
})
</script>

二、 mock.js 的依赖安装与处理

 1. 安装依赖

# 安装
npm install mockjs --save

 在 ts 中使用时,我们需要现在 shims-vue.d.ts 文件中去抛出模块,不然会出现引入报错的问题

/* eslint-disable */
declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}
declare module 'mockjs';

 2. 新建 mock 所需的文件

20210527153548522.png

index.ts(属于mockjs全局配置文件),mockjs/javaScript/index.ts(具体的数据文件),这两个需要关注,别的不用关注


 1. 新建 mockjs/javaScript/index.ts(具体的数据文件)

  因为我这里的数据主要是 侧边栏的数据,都是固定好的,所以并没有用到 mockjs 的规则生成数据

import { GlobalSidebar, Sidebar } from "../../sidebar";
namespace InfoSidebar {
    export type InfoSidebarParams = {
        body: string,
        type: string,
        url: string
    }
}
const dataSource: Array<GlobalSidebar> = [
    {
        mainTitle: 'JavaScript基础问题梳理',
        mainSidebar: [
            {
                id: 0,
                hashId: 'This',
                title: 'this指向',
                routeName: 'JsBasic',
                children: [
                    {
                        id: 1,
                        parentId: 0,
                        hashId: 'GlobalFunction',
                        title: '全局函数'
                    },
                    {
                        id: 2,
                        parentId: 0,
                        hashId: 'ObjectMethod',
                        title: '对象方法'
                    },
                    {
                        id: 3,
                        parentId: 0,
                        hashId: 'Constructor',
                        title: '构造函数'
                    },
                    {
                        id: 4,
                        parentId: 0,
                        hashId: 'SetTimeout',
                        title: '定时器、回调函数'
                    },
                    {
                        id: 5,
                        parentId: 0,
                        hashId: 'EventFunction',
                        title: '事件函数'
                    },
                    {
                        id: 6,
                        parentId: 0,
                        hashId: 'ArrowFunction',
                        title: '箭头函数'
                    },
                    {
                        id: 7,
                        parentId: 0,
                        hashId: 'CallApplyBind',
                        title: 'call、apply、bind'
                    },
                ]
            },
            {
                id: 2,
                hashId: 'DeepClone',
                title: '深拷贝和浅拷贝',
                routeName: 'JsBasic',
                children: []
            }
        ]
    },
];
export default {
    name: 'jsBasicInfo',
    jsBasicInfo(params: InfoSidebar.InfoSidebarParams) {
        const param = JSON.parse(params.body)
        if (!param) throw new Error("Params can not empty!");
        const data = dataSource.find((t: GlobalSidebar) => {
            return t.mainSidebar.filter((x: Sidebar) => {
                return x.routeName === param.routeName
            })
        })
        return {
            data,
            success: true,
            code: 200
        }
    }
} 

 Sidebar.ts

/**
 * @param { number } id Unique value
 * @param { string } hashId href Unique value
 * @param { string } title show current title
 * @param { string } routeName page find data
 */
interface GlobalSidebar {
    mainTitle: string,
    mainSidebar: Array<Sidebar>
}
interface Sidebar {
    id: number,
    hashId: string | number,
    title: string,
    routeName: string,
    children: Array<SidebarItem>,
}
interface SidebarItem {
    id: number,
    parentId: number,
    hashId: string | number,
    title: string,
}
export {
    GlobalSidebar,
    Sidebar,
    SidebarItem
}

 2. 新建 mockjs/index.ts

import Mock from "mockjs";
import jsBasicInfo from "./tpl/javaScript/index";
const requestMethod = 'post';
const BASE_URL = process.env.VUE_APP_BASE_API;
const mocks = [jsBasicInfo];
for (let i of mocks) {
    Mock.mock(BASE_URL + '/' + i.name, requestMethod, i.jsBasicInfo);
}
export default Mock

 3. main.ts 引入

import { createApp } from 'vue'
import App from './App.vue'
if(process.env.NODE_ENV == 'development'){
    require('./mockjs/index')
}
const app = createApp(App);
app.mount('#app');

三、结合使用

  实际上就是刚刚调用axios 的那一段代码

<script lang="ts" setup name="Sidebar">
import { ref, reactive, onBeforeMount } from "vue"
import { info } from "@/api"
import { useRoute } from "vue-router"
const route = useRoute();
let loading = ref<boolean>(false);
let sidebar = ref<any>({});
const _fetch = async (): Promise<void> => {
    const routeName = route.name as string;
    const path = '/' + routeName.replace(routeName[0], routeName[0].toLocaleLowerCase()) + 'Info'
    try {
        loading.value = true;
        const res = await info({ path, routeName });
        if (!res || !res.data) return;
        sidebar.value = res.data;
    } finally {
        loading.value = false
    }
}
onBeforeMount(() => {
    _fetch();
})
</script>
相关文章
|
18天前
|
缓存 前端开发 JavaScript
前端vue3分享——项目封装axios、vite使用env环境变量
前端vue3分享——项目封装axios、vite使用env环境变量
21 0
|
19天前
|
存储 JavaScript
报错permission.js:41 [Vue warn]: Property “showClose“ must be accessed with “$data.showClose“ because
报错permission.js:41 [Vue warn]: Property “showClose“ must be accessed with “$data.showClose“ because
|
20天前
|
存储 算法 JavaScript
< 今日小技巧:Axios封装,接口请求增加防抖功能 >
今天这篇文章,主要是讲述对axios封装的请求,由于部分请求可能存在延时的情况。使得接口可能存在会被持续点击(即:接口未响应的时间内,被持续请求),导致重复请求的问题,容易降低前后端服务的性能!故提出给axios封装的配置里面,新增一个防抖函数,用来限制全局请求的防抖。
< 今日小技巧:Axios封装,接口请求增加防抖功能 >
|
21天前
|
JSON JavaScript 前端开发
< 每日份知识快餐:axios是什么?如何在Vue中 封装 axios ? >
本文介绍了前端开发中常用的HTTP客户端库Axios,它基于Promise,支持浏览器和Node.js,特点是功能强大、支持Promise API和并发请求,并能拦截请求和响应。文章强调了理解Axios的内部原理和优化使用的重要性,不仅讲解了基本的安装、导入和使用方法,还阐述了为何选择Axios,包括其丰富的配置选项和良好的浏览器支持。此外,文章探讨了封装Axios的必要性,以减少重复代码和提高代码维护性,并给出了设置接口请求前缀、请求头、超时时间以及封装请求方法和拦截器的示例。通过封装,开发者可以更高效地管理和使用Axios,适应不同项目需求。
|
21天前
|
JavaScript
Vue与原生JS中方法调用
Vue与原生JS中方法调用
8 0
|
26天前
|
JavaScript 前端开发 UED
Vue工具和生态系统: Vue.js和服务器端渲染(SSR)有关系吗?请解释。
Vue.js是一个渐进式JavaScript框架,常用于开发单页面应用,但其首屏加载较慢影响用户体验和SEO。为解决此问题,Vue.js支持服务器端渲染(SSR),在服务器预生成HTML,加快首屏速度。Vue.js的SSR可手动实现或借助如Nuxt.js的第三方库简化流程。Nuxt.js是基于Vue.js的服务器端渲染框架,整合核心库并提供额外功能,帮助构建高效的应用,改善用户体验。
21 0
|
26天前
|
JavaScript 前端开发 开发者
Vue工具和生态系统: Vue.js和TypeScript可以一起使用吗?
【4月更文挑战第18天】Vue.js与TypeScript兼容,官方文档支持在Vue项目中集成TypeScript。TypeScript作为JavaScript超集,提供静态类型检查和面向对象编程,增强代码准确性和健壮性。使用TypeScript能提前发现潜在错误,提升代码可读性,支持接口和泛型,使数据结构和函数更灵活。然而,不是所有Vue插件都兼容TypeScript,可能需额外配置。推荐尝试在Vue项目中使用TypeScript以提升项目质量。
16 0
|
26天前
|
资源调度 JavaScript 前端开发
Vue工具和生态系统: 如何使用Vue.js实现服务端渲染(SSR)?不少于500字
Vue.js框架用于构建用户界面,而服务端渲染(SSR)能提升首屏加载速度和SEO。以下是使用Vue.js实现SSR的简要步骤:1) 安装vue、vue-server-renderer和express依赖;2) 创建Vue应用如`vue create my-ssr-app`;3) 使用express创建服务器;4) 在Express应用中设定路由处理所有请求;5) 创建渲染器将Vue应用转为HTML;6) 运行服务器如`node my-ssr-app/server.js`。实际应用可能涉及动态内容、状态管理和错误处理等复杂情况。
26 1
|
27天前
axios封装和配置
axios封装和配置
19 0
|
1月前
|
JavaScript
vue封装axios(用interceptors封装)
vue封装axios(用interceptors封装)
13 0