<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
今天我們一起來看一看 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
使用非同步網路請求肯定離不開loading、message 等提示,今天我們配合 elementPlus 一起使用;
// 安裝axios npm install axios --save // 安裝 elementPlus npm install element-plus --save
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)
src 目錄下新增 api/index.ts
- 定義請求的引數型別
- 定義響應想具體引數型別
這裡我們使用到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>
# 安裝 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';
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>
到此這篇關於vue3和ts封裝axios以及使用mock.js詳解的文章就介紹到這了,更多相關vue3 ts封裝axios使用mock.js內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45