建立好vue3專案
Axios安裝
npm install axios
Element Plus 安裝
官網入口:https://element-plus.gitee.io/zh-CN/
npm install element-plus --save
Element 主要用到資訊提示 與 全螢幕載入動畫
api 資料夾下 封裝 Axios封裝 與 請求設定
utils 資料夾下 operate.ts 設定介面地址 與其他全域性ts
舊版本地址:https://www.cnblogs.com/lovejielive/p/16363587.html
新版本:主要增加動態控制是否顯示載入動畫。
是否需要判斷重複請求。
優化請求介面設定引數寫法。
擴充套件AxiosRequestConfig 增加自定義引數
declare module 'axios' { //請求自定義引數 interface AxiosRequestConfig { // 是否顯示載入框 ifLoading?: boolean // 是否允許重複請求 repeatRequest?: boolean // 登入 token isToken?: any; } }
通過設定repeatRequest是否允許重複請求,來開啟判斷。主要在api.ts中設定。
每一次請求建立一個key,判斷是否存在,如存在執行.abort()取消當前請求,
不存在pendingMap中新增一個key。
通過AbortController來進行手動取消。
主要程式碼
//格式化請求連結 function getRequestKey(config: AxiosRequestConfig) { const { url, method, data, params } = config, //字串化引數 dataStr = JSON.stringify(data) || '', paramsStr = JSON.stringify(params) || '', //記得這裡一定要處理 每次請求都掉會變化的引數(比如每個請求都攜帶了時間戳),否則二個請求的key不一樣 key = [method, url, dataStr, paramsStr].join("&"); return key; } //建立儲存 key 的 集合 const pendingMap = new Map() //是否重複請求key function setPendingMap(config: AxiosRequestConfig) { //手動取消 const controller = new AbortController() config.signal = controller.signal const key = getRequestKey(config) //判斷是否存在key 存在取消請求 不存在新增 if (pendingMap.has(key)) { // abort取消請求 pendingMap.get(key).abort() //刪除key pendingMap.delete(key) } else { pendingMap.set(key, controller) } }
在介面訊息提示時,通過 axios.isCancel(error) 過濾掉已取消的請求,
//攔截掉重複請求的錯誤,中斷promise執行 if (axios.isCancel(error)) return []
/* * @description: 請求封裝 * @Author: Jay * @Date: 2023-04-11 13:24:41 * @LastEditors: Jay * @LastEditTime: 2023-09-04 14:52:09 */ // 匯入axios import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; // 使用element-ui ElMessage做訊息提醒 ElLoading載入 import { ElMessage, ElLoading } from "element-plus"; //請求頭 import operate from "@/utils/operate" //載入設定 let loadingInstance: { close: () => void }; let requestNum = 0; //載入動畫 const addLoading = () => { // 防止重複彈出 requestNum++; if (requestNum === 1) { loadingInstance = ElLoading.service({ fullscreen: true }); } } // 關閉 載入動畫 const cancelLoading = () => { requestNum--; if (requestNum === 0) loadingInstance?.close(); } //格式化請求連結 function getRequestKey(config: AxiosRequestConfig) { const { url, method, data, params } = config, //字串化引數 dataStr = JSON.stringify(data) || '', paramsStr = JSON.stringify(params) || '', //記得這裡一定要處理 每次請求都掉會變化的引數(比如每個請求都攜帶了時間戳),否則二個請求的key不一樣 key = [method, url, dataStr, paramsStr].join("&"); return key; } //建立儲存 key 的 集合 const pendingMap = new Map() //是否重複請求key function setPendingMap(config: AxiosRequestConfig) { // 手動取消請求 const controller = new AbortController() config.signal = controller.signal const key = getRequestKey(config) //判斷是否存在key 存在取消請求 不存在新增 if (pendingMap.has(key)) { // abort取消請求 pendingMap.get(key).abort() //刪除key pendingMap.delete(key) } else { pendingMap.set(key, controller) } } //增加新的請求引數型別 declare module 'axios' { //請求自定義引數 interface AxiosRequestConfig { // 是否顯示載入框 ifLoading?: boolean // 是否允許重複請求 repeatRequest?: boolean // 登入 token isToken?: any; } // 解決 型別「AxiosResponse<any, any>」上不存在屬性「code」 interface AxiosResponse<T = any> { // 請求 data 裡的一級引數 code: number; time: string; msg: string; data: T; } } //建立axios的一個範例 const axiosInstance: AxiosInstance = axios.create({ //介面統一域名 baseURL: operate.baseUrl(), //設定超時 timeout: 1000 * 30, //跨域攜帶cookie withCredentials: true, }) // 新增請求攔截器 axiosInstance.interceptors.request.use( (config) => { //載入動畫 if (config?.ifLoading) addLoading(); //是否判斷重複請求 if (!config.repeatRequest) { setPendingMap(config) } //判斷是否有token 根據自己的需求判斷 const token = config.isToken console.log("判斷是否有token", token) if (token != undefined) { //如果要求攜帶在引數中 config.params = Object.assign({}, config.params, operate.uploadParameters()) // 如果要求攜帶在請求頭中 // config.headers = Object.assign({}, config.headers, operate.uploadParameters()) } return config }, (error: AxiosError) => { return Promise.reject(error) } ) // 新增響應攔截器 axiosInstance.interceptors.response.use((response: AxiosResponse) => { const config = response.config // 關閉載入 動畫 if (config?.ifLoading) cancelLoading(); //是否登入過期 if (response.data.code == 400 || response.data.code == 401) { ElMessage.error("登入過期,請重新登入") // //清除登入快取 // store.commit("LOGOUT") // //返回首頁 // setTimeout(() => { // router.push("/"); // }, 500); return } // 返回引數 return response.data }) // 錯誤處理 axiosInstance.interceptors.response.use(undefined, (error) => { const config = error.config // 關閉載入 動畫 if (config?.ifLoading) cancelLoading(); //攔截掉重複請求的錯誤,中斷promise執行 if (axios.isCancel(error)) return [] /***** 接收到異常響應的處理開始 *****/ if (error && error.response) { // 1.公共錯誤處理 // 2.根據響應碼具體處理 switch (error.response.status) { case 400: error.message = '錯誤請求' break; case 401: error.message = '未授權,請重新登入' break; case 403: error.message = '拒絕存取' break; case 404: error.message = '請求錯誤,未找到該資源' // window.location.href = "/NotFound" break; case 405: error.message = '請求方法未允許' break; case 408: error.message = '請求超時' break; case 500: error.message = '伺服器端出錯' break; case 501: error.message = '網路未實現' break; case 502: error.message = '網路錯誤' break; case 503: error.message = '服務不可用' break; case 504: error.message = '網路超時' break; case 505: error.message = 'http版本不支援該請求' break; default: error.message = `連線錯誤${error.response.status}` } } else { // 超時處理 if (JSON.stringify(error).includes('timeout')) { error.message = '伺服器響應超時,請重新整理當前頁' } else { error.message = '連線伺服器失敗' } } //提示 ElMessage.error(error.message) /***** 處理結束 *****/ return Promise.resolve(error) }) export default axiosInstance
/* * @description: 請求介面 設定 * @Author: Jay * @Date: 2023-04-11 13:24:41 * @LastEditors: Jay * @LastEditTime: 2023-09-04 13:30:09 */ //匯入 Axios 請求 import request from '@/utils/request' //其他設定 import operate from '@/utils/operate'; // 官網介面 export const homePost = (data?: any) => { return request({ url: '/api/index', method: 'post', data, //登入token isToken: operate.isToken(), //載入動畫是否啟動 ifLoading: true, //是否允許重複請求 repeatRequest: false, }) } /* 請求設定與使用 * 請求 方式 export const 名字 = (data: any) => request.post("介面", data, { 直接為空 注:只能設定 AxiosRequestConfig 裡有的引數名 可不用設定 }); *使用 方法 *引入 import { 名字 } from "../api/api" *生命週期中 請求 名字({請求引數}).then((res) => { console.log(res) }) */
<script lang="ts" setup> import { onMounted } from "vue"; import { homePost } from "@/api/api"; //生命週期 onMounted(() => { homePost().then((res) => { console.log("第一次", res); }); homePost().then((res) => { console.log("第二次", res); }); }); </script>
請求結果
第一次請求被攔截,只有第二次成功返回
主要放置一些 全域性引數與方法。
在頁面中可以通過 import operate from "@/utils/operate"
/* * @description: 全域性js * @Author: Jay * @Date: 2023-09-04 13:53:47 * @LastEditors: Jay * @LastEditTime: 2023-09-04 13:55:44 */ // vuex 資料 import store from '../store/index' //介面地址 const baseUrl = () => { if (process.env.NODE_ENV == "development") { //開發環境 return ""; } else { //正式環境 return ""; } } //獲取使用者token const isToken = () => { if (store.state.Authorization != '') { return store.state.Authorization } return ''; } //上傳請求頭 登入驗證 const uploadParameters = () => { return { 'token': isToken() } // return { 'Authori-zation': 'Bearer ' + isToken() } } /* eslint-disable */ /* 格式化時間 加上時分秒 num: 後臺時間格式 type: 'YY-MM-DD' 年月日 ,'HH-MM-SS' 時分秒 ,不傳 年月日時分秒 */ const happenTime = (num: any, type: string) => { let date = new Date(num * 1000); //時間戳為10位需*1000,時間戳為13位的話不需乘1000 let y: any = date.getFullYear(); let MM: any = date.getMonth() + 1; MM = MM < 10 ? ('0' + MM) : MM; //月補0 let d: any = date.getDate(); d = d < 10 ? ('0' + d) : d; //天補0 let h: any = date.getHours(); h = h < 10 ? ('0' + h) : h; //小時補0 let m: any = date.getMinutes(); m = m < 10 ? ('0' + m) : m; //分鐘補0 let s: any = date.getSeconds(); s = s < 10 ? ('0' + s) : s; //秒補0 if (type === 'YY-MM-DD') { //年月日 return y + '-' + MM + '-' + d; } else if (type === 'HH-MM-SS') { //時分秒 return h + ':' + m + ':' + s; } else { //全部 return y + '-' + MM + '-' + d + ' ' + h + ':' + m + ':' + s; } } /* eslint-enable */ // 頁面回到頂部(捲動效果) /* 使用方法 //監聽捲動事件 window.addEventListener("scroll", proxy.$operate.handleScroll, { once: true, }); */ const handleScroll = () => { let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; console.log(scrollTop, "scrollTop"); if (scrollTop > 0) { const timeTop = setInterval(() => { document.documentElement.scrollTop = document.body.scrollTop = scrollTop -= 50; //一次減50往上滑動 if (scrollTop <= 0) { clearInterval(timeTop); } }, 10); //定時呼叫函數使其更順滑 } }; export default { baseUrl, isToken, uploadParameters, happenTime, handleScroll }
本文來自部落格園,作者:虛乄,轉載請註明原文連結:https://www.cnblogs.com/lovejielive/p/17676856.html