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

news2024/11/16 16:42:30

今天我们一起来看一看 vue3+ts如何优雅的封装axios,并结合 mock.js 实现敏捷开发;

但是我们要注意区分 Axios 和 Ajax :

    Ajax 是一种技术统称,技术内容包括:HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 以及最重要的XMLHttpRequest,用于浏览器与服务器之间使用异步数据传输(HTTP 请求),做到局部请求以实现局部刷新,使用是基于 XMLHttpRequest 进行使用;

  Axios 是 一个基于 promise 的 HTTP 库,是一个是第三方库

今天主要技术栈:vue3,ts,axios,mock.js,elementPlus

目录

一、axios 的依赖安装与处理

  1. 依赖安装

  2. 全局 axios 封装

  3. 实际使用

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

  1. 安装依赖

  2. 新建 mock 所需的文件

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

  2. 新建 mockjs/index.ts 

  3. main.ts 引入

三、结合使用


一、axios 的依赖安装与处理

  1. 依赖安装

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

// 安装axios 

npm install axios --save


// 安装 elementPlus

npm install element-plus --save

        

  2. 全局 axios 封装

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

  1. 定义请求数据返回的格式,需要提前确认好
  2. 定义 axios 基础配置信息
  3. 请求拦截器:所有请求最先到达的地方,我们可以在此自定义请求头信息(比如:token、多语言等等)
  4. 响应拦截器:返回数据最先到达的地方,我们可以在此处理异常信息(比如: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 所需的文件

 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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/409844.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue首次加载太慢之性能优化

Vue首次加载太慢之性能优化前言一、防止编译文件中出现map文件二、vue-router 路由懒加载三、使用gzip压缩四、使用CDN加载第三方库第一步&#xff1a;引入资源第二步&#xff1a;添加配置第三步&#xff1a;去掉原有的引用五、去掉代码中的console.log前言 首页加载很慢的问题…

Vite4 + Vue3 + vue-router4 动态路由

动态路由&#xff0c;基本上每一个项目都能接触到这个东西&#xff0c;通俗一点就是我们的菜单是根据后端接口返回的数据进行动态生成的。表面上是对菜单的一个展现处理&#xff0c;其实内部就是对router的一个数据处理。当然你只对菜单做处理也是可以的&#xff0c;但是没有任…

js中的内存泄漏

简版 内存泄漏一般是指变量的内存没有及时的回收&#xff0c;导致内存资源浪费。一般有三种情况出现内存泄露比较多。&#xff08;1&#xff09;常见的声明了一个全局变量&#xff0c;但是又没有用上&#xff0c;那么就有点浪费内存了&#xff0c;&#xff08;2&#xff09;定…

【Vue3】用Element Plus实现列表界面

&#x1f3c6;今日学习目标&#xff1a;用Element Plus实现列表界面 &#x1f603;创作者&#xff1a;颜颜yan_ ✨个人格言&#xff1a;生如芥子&#xff0c;心藏须弥 ⏰本期期数&#xff1a;第四期 &#x1f389;专栏系列&#xff1a;Vue3 文章目录前言效果图目录简介修改vite…

uniapp微信小程序实现连接低功耗蓝牙打印功能

微信小程序项目中有使用到蓝牙连接打印&#xff0c;参考官方文档做了一个参考笔记&#xff0c;这样使用的时候就按着步骤查看。 uni-app蓝牙连接 蓝牙&#xff1a; 1、初始化蓝牙 uni.openBluetoothAdapter(OBJECT) uni.openBluetoothAdapter({success(res) {console.log(…

毫米波雷达的硬件架构与射频前端

说明 本篇博文梳理(车载)毫米波雷达的系统构成&#xff0c;特别地&#xff0c;对其射频前端各部件做细节性的原理说明。本篇博文会基于对这方面知识理解的加深以及读者的反馈长期更新内容和所附资料&#xff0c;有不当之处或有其它有益的参考资料可以在评论区给出&#xff0c;我…

vue3.x结合element-plus如何使用icon图标

基于 Vue 3的Element Plus如何使用icon图标 首先注意Element Plus版本&#xff1a;官网如图所示&#xff0c; 基于vue3的具体如何使用&#xff1a; 参考官网文档&#xff1a; 1.首先选择一种方式安装 2.然后全局注册图标 在main.js或main.ts文件中引入&#xff1a; import …

【web前端开发】CSS文字和文本样式

文章目录前言字体大小字体粗细字体样式字体font复合属性文本缩进文本水平对齐方式文本修饰线行高颜色标签水平居中⭐思维导图前言 本篇文章主要讲解CSS中的文字和文本样式及一些实用技巧.文章最后有思维导图. 字体大小 属性名:font-size 取值: 数字px 注意: 谷歌浏览器默认…

无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/jstl/core]

这是我在学习 JSTL 时产生的错误。没有使用 Maven 。 是手动引入 JSTL 包&#xff08;jstl.jar 和 standard.jar&#xff09;。在请求转发到 .jsp 界面&#xff0c;再没有使用 JSTL 语句前是通的&#xff0c;使用 JSTL 后就会报错。org.apache.jasper.JasperException: 无法在w…

【Vue】filter 过滤器用法

文章目录一、全局过滤器二、局部过滤器一、全局过滤器 语法&#xff1a; Vue.filter(name,function) - name: 过滤器名称 - function: 执行函数&#xff0c;必须有一个过滤后的返回值使用方法&#xff1a;变量名 | 过滤器名称 {{name | function()}}示例&#xff1a; <!D…

webpack的安装

webpack是当前前端最热门的前端资源模块化管理和打包工具。 安装webpack 首先要安装Node.js&#xff0c;Node.js自带了软件包管理工具npm&#xff0c;可以使用npm安装各种插件。Webpack至少需要Node.js v0.6版本&#xff0c;直接安装最新版就好了。 1.nodeJs官网&#xff0c…

Luckysheet本地导入时报错‘luckysheet‘ is not defined【已解决】

本文包含luckysheet本地安装以及报错解决&#xff0c;性子急可以直接按照目录划拉&#xff01; 目录 一、本地导入 二、报错luckysheet is not defined解决 最近正在开发的项目中需要类excel功能的东西&#xff0c;经过调研后决定用luckysheet&#xff0c;Luckysheet在线表格…

vite + vue3 —— vue地图大屏项目

​回顾 前期 ​ 前端利器 —— 提升《500倍开发效率》 传一张设计稿&#xff0c;点击一建生成项目 好牛_0.活在风浪里的博客-CSDN博客如果非要说它有什么缺点&#xff0c;那么我觉得就是它会&#xff0c;让你cv大法都没处使&#xff01;&#xff01;&#xff01;比如…

React 面向组件编程(下)

目录前言&#xff1a;一、受控组件与非受控组件1. 受控组件2. 非受控组件3. 效果展示4. 总结&#xff1a;二、组件的生命周期1. 对生命周期的理解2. 生命周期的三个阶段&#xff08;旧&#xff09;3. 生命周期的三个阶段&#xff08;新&#xff09;4. 新旧生命周期的区别5. 重要…

微信小程序项目实例——扫雷

今日推荐&#x1f481;‍♂️ 2023许嵩演唱会即将到来&#x1f3a4;&#x1f3a4;&#x1f3a4;大家一起冲冲冲&#x1f3c3;‍♂️&#x1f3c3;‍♂️&#x1f3c3;‍♂️ &#x1f52e;&#x1f52e;&#x1f52e;&#x1f52e;&#x1f52e;往期优质项目实例&#x1f52e…

JavaWeb之Cookie详解(一)

一.Cookie简介 Cookie是在HTTP协议下&#xff0c;服务器维护客户工作站上信息的一种方式&#xff0c;其作用是维护服务端与客户端浏览器的会话状态。由于HTTP协议的特点是无状态性&#xff0c;它对于事务处理没有记忆能力&#xff0c;所以服务端不会记录当前客户端浏览器的访问…

image-conversion 图片压缩,vue

image-conversion &#xff0c;vue&#xff0c;element 需求&#xff1a;需要用户上传图片时候&#xff0c;对大尺寸图片进行压缩&#xff0c;减小图片大小&#xff0c;减少服务器的压力。 用到的第三方插件image-conversion 文档&#xff1a; https://www.npmjs.com/packag…

vue2 前端导出el-table表格为Excel文件 (自带样式)

前话&#xff1a;我只是个自学到工作的小菜鸟&#xff0c;如果有大佬有更好的方法&#xff0c;希望不吝赐教。 1.电脑环境 node版本 14.20.0 vue-cli 4.4.0 (如果你的项目是vue-cli 5 版本&#xff08;即webpack5&#xff09;的会有很多问题&#xff0c;解决办法写在后边)…

echarts:实现3D地图版块叠加动效散点+轮播高亮效果

需求描述 如下图所示&#xff0c;展示3D效果的地图版块&#xff0c;并叠加显示动效散点&#xff1a; 实现思路 首先是3D地图版块效果的实现&#xff0c;可以参考广州3D地图&#xff1b;而动效散点的实现&#xff0c;可以参考地图发散分布。 这里再提一个经过尝试并不行的思…

大学生web前端期末大作业实例代码 (1500套,建议收藏) HTML+CSS+JS

文章目录&#x1f4da;web前端期末大作业 (1500套) 集合一、网页介绍二、网页集合&#x1f48c;表白网页 125套 (集合)&#x1f499;Echarts大屏数据展示 150套 (集合)一、基于HTMLEcharts技术制作二、基于VUEEcharts技术制作&#x1f381;更多源码&#x1f4da;web前端期末大作…