Vue3+JS 組合式 API 實戰:從項目痛點到通用 Hook 封裝

Vue3 組合式 API 的實戰技巧 —— 組合式 API 幫我解決了不少 Options API 難以應對的問題,尤其是在代碼復用和復雜組件維護上。

一、為什么放棄 Options API?聊聊三年項目里的真實痛點?

剛接觸 Vue3 時,我曾因 “慣性” 繼續用 Options API 寫業務,但隨著項目復雜度提升,兩個痛點越來越明顯:?

  1. 代碼碎片化:一個數據請求相關的邏輯(加載態、數據處理、錯誤提示),要分散在data、methods、mounted里,后期維護時需在多個選項間跳轉,尤其復雜表單組件,邏輯溯源成本極高;?
  2. 復用性差:比如多個組件都需要 “分頁加載列表” 功能,Options API 只能通過mixins實現,但mixins存在命名沖突、邏輯來源不清晰的問題(比如同事接手項目時,不知道某個變量是來自組件本身還是mixins)。?

直到用組合式 API 重構了第一個列表頁,才真正體會到 “邏輯聚合” 的爽 —— 所有和 “列表請求” 相關的代碼都放在一起,復用只需復制一個函數,這也是我今天想重點分享的核心:用組合式 API 封裝通用 Hook,解決重復造輪子問題。?

二、實戰:封裝 2 個 Vue3+JS 通用 Hook(附完整代碼)?

下面結合我項目中高頻使用的場景,分享兩個通用 Hook 的封裝思路,所有代碼均基于 Vue3+JS 編寫,可直接復制到項目中使用。?

1. useRequest:統一處理請求狀態(加載 / 成功 / 失敗)?

幾乎所有業務組件都需要請求接口,而 “加載態顯示”“錯誤提示”“請求取消” 是通用需求。之前每個組件都要寫loading: false、error: '',重復代碼占比 30% 以上,用useRequest可完全統一。?

代碼實現:

import { ref, onUnmounted } from 'vue';
import { ElMessage } from 'element-plus'; // 假設項目用Element Plus,可替換為其他UI庫/*** 通用請求Hook* @param {Function} requestFn - 接口請求函數(需返回Promise)* @param {Object} options - 配置項(可選)* @param {boolean} options.autoRun - 是否默認執行請求(默認true)* @param {Function} options.onSuccess - 請求成功回調* @param {Function} options.onError - 請求失敗回調*/
export function useRequest(requestFn, options = {}) {const { autoRun = true, onSuccess, onError } = options;const loading = ref(false); // 加載態const data = ref(null); // 請求結果const error = ref(''); // 錯誤信息const controller = new AbortController(); // 用于取消請求// 核心請求函數const fetchData = async (...args) => {loading.value = true;error.value = '';try {// 傳遞signal,支持取消請求const result = await requestFn(...args, { signal: controller.signal });data.value = result;onSuccess && onSuccess(result); // 自定義成功回調} catch (err) {// 排除手動取消請求的錯誤if (err.name !== 'AbortError') {error.value = err.message || '請求失敗,請重試';ElMessage.error(error.value); // 全局錯誤提示onError && onError(err); // 自定義失敗回調}} finally {loading.value = false;}};// 自動執行請求(默認開啟)if (autoRun) {fetchData();}// 組件卸載時取消請求,避免內存泄漏onUnmounted(() => {controller.abort();});return {loading,data,error,fetchData, // 手動觸發請求(比如刷新按鈕)cancelRequest: () => controller.abort() // 手動取消請求};
}

使用示例(用戶列表組件):

import { useRequest } from '@/hooks/useRequest';
import { getUserList } from '@/api/user'; // 接口函數export default {setup() {// 1. 初始化請求Hook,傳入接口函數和配置const { loading, data: userList, fetchData } = useRequest(getUserList, {onSuccess: (res) => {console.log('用戶列表請求成功', res);}});// 2. 手動刷新(比如搜索按鈕點擊)const handleRefresh = (searchParams) => {fetchData(searchParams); // 傳遞參數給接口函數};return {loading,userList,handleRefresh};}
};
2. usePagination:快速實現分頁功能?

管理后臺中 “分頁列表” 是高頻場景,頁碼切換、每頁條數變更、總數計算這些邏輯完全可以復用。usePagination可結合上面的useRequest,快速搭建分頁功能。?

代碼實現:

import { ref, computed } from 'vue';/*** 通用分頁Hook* @param {Function} fetchFn - 列表請求函數(需接收page、pageSize參數)* @param {Object} defaultParams - 默認分頁參數(可選)*/
export function usePagination(fetchFn, defaultParams = {}) {// 分頁基礎參數const page = ref(defaultParams.page || 1); // 當前頁碼const pageSize = ref(defaultParams.pageSize || 10); // 每頁條數const total = ref(0); // 總條數// 計算總頁數const totalPage = computed(() => Math.ceil(total.value / pageSize.value) || 1);// 頁碼切換事件const handlePageChange = (newPage) => {page.value = newPage;fetchFn({ page: newPage, pageSize: pageSize.value }); // 觸發請求};// 每頁條數變更事件const handlePageSizeChange = (newSize) => {pageSize.value = newSize;page.value = 1; // 重置為第一頁fetchFn({ page: 1, pageSize: newSize }); // 觸發請求};// 重置分頁參數(比如搜索時)const resetPagination = () => {page.value = 1;pageSize.value = defaultParams.pageSize || 10;};return {page,pageSize,total,totalPage,handlePageChange,handlePageSizeChange,resetPagination,// 分頁參數對象(方便傳遞給請求函數)paginationParams: computed(() => ({page: page.value,pageSize: pageSize.value}))};
}

結合 useRequest 使用示例:

import { useRequest } from '@/hooks/useRequest';
import { usePagination } from '@/hooks/usePagination';
import { getUserList } from '@/api/user';export default {setup() {// 1. 初始化分頁Hook,定義請求函數(接收分頁參數)const { paginationParams, total, handlePageChange, handlePageSizeChange } = usePagination(async (params) => {// 調用useRequest的fetchData,傳遞分頁參數await fetchData({ ...params, ...searchParams.value });});// 2. 初始化請求Hook,請求時帶上分頁參數const searchParams = ref({}); // 搜索參數(可選)const { loading, data: userList, fetchData } = useRequest(async (params = {}) => {const res = await getUserList({ ...paginationParams.value, ...params });total.value = res.total; // 更新總條數return res.list;});// 3. 搜索功能(重置分頁)const handleSearch = (params) => {searchParams.value = params;handlePageChange(1); // 搜索時跳轉到第一頁};return {loading,userList,page: paginationParams.page,pageSize: paginationParams.pageSize,total,handlePageChange,handlePageSizeChange,handleSearch};}
};

三、Vue3+JS 開發的 3 個避坑技巧(三年經驗總結)?

  1. 避免在 setup 中直接修改 props:Vue3 中 props 仍是單向數據流,若需修改 props,可通過emit通知父組件,或用toRef創建 props 的響應式引用(如const name = toRef(props, 'name'));?
  2. watch 監聽對象時要加 deep:若監聽的是對象 / 數組,需開啟deep: true才能監聽到內部屬性變化(如watch(user, () => {}, { deep: true })),但盡量避免監聽整個對象,可直接監聽具體屬性(如watch(() => user.name, () => {})),性能更好;?
  3. Hook 命名規范統一:自定義 Hook 建議以use開頭(如useRequest、usePagination),方便團隊識別和維護,避免出現getPagination、paginationUtil這類不統一的命名。?

四、總結與交流?

以上就是我基于 Vue3+JS 棧的實戰分享 —— 從項目痛點出發,用組合式 API 封裝通用 Hook,既能減少重復代碼,又能提升項目可維護性。這也是我三年前端開發中,從 “寫功能” 到 “寫優雅的功能” 的一個重要轉變。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/98758.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/98758.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/98758.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

把 AI 塞進「電梯按鈕」——基于 64 kB 零樣本聲紋的離線故障預測按鈕

標簽:零樣本聲紋、電梯按鈕、離線 AI、TinyML、RISC-V、低功耗、GD32V303、故障預警 ---- 1. 背景:為什么按鈕要「聽聲音」? 全國 700 萬臺電梯,按鈕故障率 0.3 %/年,卻常出現: ? 機械卡滯、觸點氧化&…

清華大學聯合項目 論文解讀 | MoTo賦能雙臂機器人:實現零樣本移動操作

研究背景 移動操作是機器人領域的核心挑戰,它使機器人能夠在各種任務和動態日常環境中為人類提供幫助。傳統的移動操作方法由于缺乏大規模訓練,往往難以在不同任務和環境中實現泛化。而現有操作基礎模型雖在固定基座任務中表現出強泛化性,卻無…

go webrtc - 2 webrtc重要概念

webrtc是一套音視頻傳輸技術生態,不是一個協議或一個什么東西。3種模式本文基于 SFU 形式闡述!重要概念:sfu 服務負責:信令 服務負責:peerConnection:track:房間:虛擬分組概念用戶&a…

“下游任務”概念詳解:從定義到應用場景

“下游任務”概念詳解:從定義到應用場景 一、什么是“下游任務”? 在機器學習(尤其是深度學習)中,“下游任務”(Downstream Task)是相對“上游過程”而言的目標任務——可以理解為:我…

視頻怎么做成 GIF?用 oCam 一鍵錄制 GIF 動畫超簡單

GIF 動圖因其生動直觀、無需點擊播放的特點,越來越受歡迎。你是否也曾看到一段有趣的視頻,想把它做成 GIF 發給朋友或用在PPT里?其實,將視頻片段轉換為 GIF 并不需要復雜的視頻剪輯技術,使用一款支持直接錄制為 GIF 的…

Vue.config.js中的Webpack配置、優化及多頁面應用開發

Vue.config.js中的Webpack配置、優化及多頁面應用開發 在Vue CLI 3項目中,vue.config.js文件是工程化配置的核心入口,它通過集成Webpack配置、優化策略和多頁面開發支持,為項目構建提供高度可定制化的解決方案。本文將從基礎配置、性能優化、…

行業學習【電商】:直播電商的去頭部化、矩陣號?

聲明:以下部分內容含AI生成這兩個詞是當前直播電商和MCN領域的核心戰略,理解了它們就理解了行業正在發生的深刻變化。一、如何理解“去頭部化”?“去頭部化” 指的是平臺或MCN機構有意識地減少對超頭部主播(如曾經的李佳琦、薇婭&…

【MFC視圖和窗口基礎:文檔/視圖的“雙胞胎”魔法 + 單文檔程序】

大家好,我是你的MFC編程小伙伴!學MFC就像探險古墓:到處是神秘的“房間”(窗口)和“寶藏”(數據)。今天咱們聊聊核心概念 – 視圖、窗口和文檔。這些是MFC的“骨架”,懂了它們&#x…

深度學習(六):代價函數的意義

在深度學習的浩瀚世界中,代價函數(Cost Function),又稱損失函數(Loss Function)或目標函數(Objective Function),扮演著至關重要的角色,它就像一個導航員&…

Kable使用指南:Android BLE開發的現代化解決方案

概述 Kable(com.juul.kable:core)是一個專為Android藍牙低功耗(BLE)開發設計的Kotlin協程友好庫。它通過提供簡潔的API和響應式編程模式,極大地簡化了BLE設備交互的復雜性。本文將詳細介紹Kable的使用方法,…

Android圖案解鎖繪制

使用到的庫是Pattern Locker,根據示例進行了修改,把默認樣式和自定義樣式進行了合并調整。 設置密碼 布局 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xm…

Kotlin 協程之 Flow 的理解使用及源碼解析

前言 在前面的文章中&#xff0c;我們已經討論了 Channel 的概念和基本使用以及 Channel 的高階應用。這篇我們來看日常開發中更常用的Flow。 “冷流” 和 “熱流” 的本質 先來梳理一下所謂的 “冷流” 和 “熱流”。 核心概念 我們已經知道 Channel 是 “熱流”&#xff…

簡述ajax、node.js、webpack、git

本系列可作為前端學習系列的筆記&#xff0c;HTML、CSS和JavaScript系列文章 已經收錄在前端專欄&#xff0c;有需要的寶寶們可以點擊前端專欄查看&#xff01; 點贊關注不迷路&#xff01;您的點贊、關注和收藏是對小編最大的支持和鼓勵&#xff01; 系列文章目錄 簡述ajax、…

經營幫會員經營:全方位助力企業高效發展,解鎖商業新可能

在商業競爭愈發激烈的當下&#xff0c;企業若想脫穎而出&#xff0c;高效的經營管理體系至關重要。經營幫的會員經營板塊&#xff0c;憑借豐富且實用的功能&#xff0c;為企業打造了一站式的經營助力平臺&#xff0c;從多維度賦能企業&#xff0c;讓發展之路更順暢。會員經營與…

Vue 封裝Input組件 雙向通信

子組件<template><div class"box"><div class"box-left"><input blur"handleBlur" v-model"localInput" class"box-left-input"> </div><div class"box-right"><p style…

伽馬(gamma)變換記錄

此只記錄伽馬變換原理及其應用結果&#xff08;文章所有內容基于數字圖像處理-岡薩雷斯&#xff09;&#xff0c;和直接用MATLAB代碼生成伽馬變換代碼。一、原理伽馬變換的公式很簡答 就是一個有規律的冪運算 公式如下&#xff1a;一般在圖像中進行應用是 C1 y為不同值時r的輸…

電路學習(六)三極管

三極管是一種電流驅動元器件&#xff08;MOS管為電壓驅動&#xff09;&#xff0c;在電路中可以充當開關&#xff0c;放大電流等作用。本文章參考了尚硅谷的視頻資料。1. 什么是三極管&#xff1f;三極管又被稱為晶體三極管&#xff08;Bipolar Junction Transistor&#xff0c…

配置docker常見問題

輸入sudo yum install -y yum-utils device-mapper-persistent-data lvm2出現Cannot find a valid baseurl for repo: base/7/x86_64一、檢查網絡輸入ping www.baidu.com出現PING www.a.shifen.com (220.181.111.1) 56(84) bytes of data. 64 bytes from 220.181.111.1 (220.18…

Python 實戰:票據圖像自動矯正技術拆解與落地教程

在日常辦公自動化&#xff08;OA&#xff09;或財務數字化場景中&#xff0c;拍攝的票據常因角度問題出現傾斜、變形&#xff0c;不僅影響視覺呈現&#xff0c;更會導致 OCR 文字識別準確率大幅下降。本文將從技術原理到代碼實現&#xff0c;手把手教你用 Python 打造票據圖像自…

vue3+TS項目配置unocss

配置unocss &#xff08;1&#xff09;安裝依賴 npm i unocss unocss/preset-uno unocss/preset-attributify -D npm install unocss/transformer-directives&#xff08;2&#xff09;根目錄新建uno.config.ts文件 import { defineConfig } from "unocss"; impor…