vue3和ts封裝axios以及使用mock.js詳解
前言
今天我們一起來(lái)看一看 vue3+ts如何優(yōu)雅的封裝axios,并結(jié)合 mock.js 實(shí)現(xiàn)敏捷開(kāi)發(fā);
但是我們要注意區(qū)分 Axios 和 Ajax :
Ajax 是一種技術(shù)統(tǒng)稱,技術(shù)內(nèi)容包括:HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 以及最重要的XMLHttpRequest,用于瀏覽器與服務(wù)器之間使用異步數(shù)據(jù)傳輸(HTTP 請(qǐng)求),做到局部請(qǐng)求以實(shí)現(xiàn)局部刷新,使用是基于 XMLHttpRequest 進(jìn)行使用;
Axios 是 一個(gè)基于 promise 的 HTTP 庫(kù),是一個(gè)是第三方庫(kù)
今天主要技術(shù)棧:vue3,ts,axios,mock.js,elementPlus
一、axios 的依賴安裝與處理
1. 依賴安裝
使用異步網(wǎng)絡(luò)請(qǐng)求肯定離不開(kāi)loading、message 等提示,今天我們配合 elementPlus 一起使用;
// 安裝axios npm install axios --save // 安裝 elementPlus npm install element-plus --save
2. 全局 axios 封裝
src 目錄下 utils 目錄下,新建 request.ts,因?yàn)槭褂玫氖荰S,需要提前定義數(shù)據(jù)格式:
- 定義請(qǐng)求數(shù)據(jù)返回的格式,需要提前確認(rèn)好
- 定義 axios 基礎(chǔ)配置信息
- 請(qǐng)求攔截器:所有請(qǐng)求最先到達(dá)的地方,我們可以在此自定義請(qǐng)求頭信息(比如:token、多語(yǔ)言等等)
- 響應(yīng)攔截器:返回?cái)?shù)據(jù)最先到達(dá)的地方,我們可以在此處理異常信息(比如:code為401重定向至登錄、code為500提示錯(cuò)誤信息)
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, // 請(qǐng)求超時(shí) request timeout
FAIL = 500, // 服務(wù)器異常 server error
LOGINTIMEOUT = 401, // 登錄超時(shí) login timeout
SUCCESS = 200, // 請(qǐng)求成功 request successfully
}
// axios 基礎(chǔ)配置
const config = {
// 默認(rèn)地址,可以使用 process Node內(nèi)置的,項(xiàng)目根目錄下新建 .env.development
baseURL: process.env.VUE_APP_BASE_API as string,
timeout: RequestEnums.TIMEOUT as number, // 請(qǐng)求超時(shí)時(shí)間
withCredentials: true, // 跨越的時(shí)候允許攜帶憑證
}
class Request {
service: AxiosInstance;
constructor(config: AxiosRequestConfig) {
// 實(shí)例化 serice
this.service = axios.create(config);
/**
* 請(qǐng)求攔截器
* request -> { 請(qǐng)求攔截器 } -> server
*/
this.service.interceptors.request.use(
(config: AxiosRequestConfig) => {
const token = localStorage.getItem('token') ?? '';
return {
...config,
headers: {
'customToken': "customBearer " + token
}
}
},
(error: AxiosError) => {
// 請(qǐng)求報(bào)錯(cuò)
Promise.reject(error)
}
);
/**
* 響應(yīng)攔截器
* response -> { 響應(yīng)攔截器 } -> client
*/
this.service.interceptors.response.use(
(response: AxiosResponse) => {
const { data, config } = response;
if (data.code === RequestEnums.LOGINTIMEOUT) {
// 表示登錄過(guò)期,需要重定向至登錄頁(yè)面
ElMessageBox.alert("Session expired", "System info", {
confirmButtonText: 'Relogin',
type: 'warning'
}).then(() => {
// 或者調(diào)用 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("網(wǎng)絡(luò)連接失敗,請(qǐng)檢查網(wǎng)絡(luò)");
// 可以重定向至404頁(yè)面
}
}
)
}
public handleCode = (code: number): void => {
switch (code) {
case 401:
ElMessage.error("登陸失敗,請(qǐng)重新登錄");
break;
case 500:
ElMessage.error("請(qǐng)求異常,請(qǐng)聯(lián)系管理員");
break;
default:
ElMessage.error('請(qǐng)求失敗');
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. 實(shí)際使用
src 目錄下新增 api/index.ts
- 定義請(qǐng)求的參數(shù)類型
- 定義響應(yīng)想具體參數(shù)類型
這里我們使用到ts 中的 namespace ,實(shí)際開(kāi)發(fā)中我們很多 api 可能會(huì)出現(xiàn)相同名字不同含義,所以我們使用 namespace 進(jìn)行定義
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!')
// 這里因?yàn)槭侨值囊粋€(gè)info,根據(jù)路由地址去請(qǐng)求側(cè)邊欄,所需不用把地址寫(xiě)死
return request.post<System.Sidebar>(params.path, { routeName: params.routeName })
}Vue 文件中調(diào)用
<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 中使用時(shí),我們需要現(xiàn)在 shims-vue.d.ts 文件中去拋出模塊,不然會(huì)出現(xiàn)引入報(bào)錯(cuò)的問(wèn)題
/* 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(具體的數(shù)據(jù)文件),這兩個(gè)需要關(guān)注,別的不用關(guān)注
1. 新建 mockjs/javaScript/index.ts(具體的數(shù)據(jù)文件)
因?yàn)槲疫@里的數(shù)據(jù)主要是 側(cè)邊欄的數(shù)據(jù),都是固定好的,所以并沒(méi)有用到 mockjs 的規(guī)則生成數(shù)據(jù)
import { GlobalSidebar, Sidebar } from "../../sidebar";
namespace InfoSidebar {
export type InfoSidebarParams = {
body: string,
type: string,
url: string
}
}
const dataSource: Array<GlobalSidebar> = [
{
mainTitle: 'JavaScript基礎(chǔ)問(wèn)題梳理',
mainSidebar: [
{
id: 0,
hashId: 'This',
title: 'this指向',
routeName: 'JsBasic',
children: [
{
id: 1,
parentId: 0,
hashId: 'GlobalFunction',
title: '全局函數(shù)'
},
{
id: 2,
parentId: 0,
hashId: 'ObjectMethod',
title: '對(duì)象方法'
},
{
id: 3,
parentId: 0,
hashId: 'Constructor',
title: '構(gòu)造函數(shù)'
},
{
id: 4,
parentId: 0,
hashId: 'SetTimeout',
title: '定時(shí)器、回調(diào)函數(shù)'
},
{
id: 5,
parentId: 0,
hashId: 'EventFunction',
title: '事件函數(shù)'
},
{
id: 6,
parentId: 0,
hashId: 'ArrowFunction',
title: '箭頭函數(shù)'
},
{
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 Mock3. 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');三、結(jié)合使用
實(shí)際上就是剛剛調(diào)用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>總結(jié)
到此這篇關(guān)于vue3和ts封裝axios以及使用mock.js詳解的文章就介紹到這了,更多相關(guān)vue3 ts封裝axios使用mock.js內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
VUE2.0+Element-UI+Echarts封裝的組件實(shí)例
下面小編就為大家分享一篇VUE2.0+Element-UI+Echarts封裝的組件實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
django+vue實(shí)現(xiàn)注冊(cè)登錄的示例代碼
這篇文章主要介紹了django+vue實(shí)現(xiàn)注冊(cè)登錄的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
Vue3實(shí)現(xiàn)provide/inject的示例詳解
Vue3 的 Provide / Inject 的實(shí)現(xiàn)原理其實(shí)就是巧妙利用了原型和原型鏈來(lái)實(shí)現(xiàn)的。本文將通過(guò)示例為大家介紹下provide/inject的具體實(shí)現(xiàn),需要的可以參考一下2022-11-11
vue中的v-show,v-if,v-bind的使用示例詳解
這篇文章主要介紹了vue中的v-show,v-if,v-bind的使用,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04
select的change方法傳遞多個(gè)參數(shù)的方法詳解
element-ui中的select,checkbox等組件的change方法的回調(diào)函數(shù)只有當(dāng)前選擇的val,如果想再傳入自定義參數(shù)怎么辦,本文給大家分享select的change方法如何傳遞多個(gè)參數(shù),感興趣的朋友一起看看吧2024-02-02
在Vue中創(chuàng)建可重用的 Transition的方法
這篇文章主要介紹了在Vue中創(chuàng)建可重用的 Transition,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06
Vue實(shí)現(xiàn)按鈕旋轉(zhuǎn)和移動(dòng)位置的實(shí)例代碼
這篇文章主要介紹了Vue實(shí)現(xiàn)按鈕旋轉(zhuǎn)和移動(dòng)位置的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08
Vue3使用vant檢索組件van-search遇到的問(wèn)題小結(jié)
當(dāng)清空按鈕與檢索按鈕同時(shí)居右時(shí),點(diǎn)擊clear清空按鈕事件時(shí)會(huì)同時(shí)觸發(fā)click-right-icon事件,這個(gè)時(shí)候容易觸發(fā)一系列問(wèn)題,小編小編給大家分享Vue3使用vant檢索組件van-search遇到的問(wèn)題小結(jié),感興趣的朋友一起看看吧2024-02-02

