Vue3自定義Hooks函數(shù)的使用詳解
hooks是什么
vue3 中的 hooks 就是函數(shù)的一種寫法,就是將文件的一些單獨(dú)功能的js代碼進(jìn)行抽離出來(lái)進(jìn)行封裝使用。
它的主要作用是Vue3借鑒了React的一種機(jī)制,用于在函數(shù)組件中共享狀態(tài)邏輯和副作用,從而實(shí)現(xiàn)代碼的可復(fù)用性。
注意:其實(shí) hooks 和 vue2 中的 mixin 有點(diǎn)類似,但是相對(duì) mixins 而言, hooks 更清楚復(fù)用功能代碼的來(lái)源, 更清晰易懂。
hooks的優(yōu)點(diǎn)
hooks作為獨(dú)立邏輯的組件封裝,其內(nèi)部的屬性、函數(shù)等和外部組件具有響應(yīng)式依附的作用。
自定義 hook 的作用類似于 vue2 中的 mixin 技術(shù),使用方便,易于上手。
使用 Vue3 的組合 API 封裝的可復(fù)用,高內(nèi)聚低耦合。
自定義hook需要滿足的規(guī)范
1、具備可復(fù)用功能,才需要抽離為hooks獨(dú)立文件
2、函數(shù)名/文件名以u(píng)se開頭,形如: useXX
3、引用時(shí)將響應(yīng)式變量或者方法顯式解構(gòu)暴露出來(lái);
示例如下:
const { nameRef,Fn }= useXX()
hooks和utils區(qū)別
相同點(diǎn): 通過(guò)hooks和utils函數(shù)封裝, 可以實(shí)現(xiàn)組件間共享和復(fù)用,提高代碼的可重用性和可維護(hù)性。
異同點(diǎn):
1.表現(xiàn)形式不同: hooks是在 utils 的基礎(chǔ)上再包一層組件級(jí)別的東西(鉤子函數(shù)等);utils一般用于封裝相應(yīng)的邏輯函數(shù),沒有組件的東西;
2.數(shù)據(jù)是否具有響應(yīng)式: hooks 中如果涉及到 ref,reactive,computed 這些 api 的數(shù)據(jù),是具有響應(yīng)式的;而 utils 只是單純提取公共方法就不具備響應(yīng)式;
3.作用范圍不同: hooks封裝,可以將組件的狀態(tài)和生命周期方法提取出來(lái),并在多個(gè)組件之間共享和重用;utils通常是指一些輔助函數(shù)或工具方法,用于實(shí)現(xiàn)一些常見的操作或提供特定功能。
總結(jié):
utils是通用的工具函數(shù),而hooks是對(duì)utils的一種封裝,用于在組件中共享狀態(tài)邏輯和副作用。
通過(guò)使用hooks,您可以簡(jiǎn)化代碼,并使其更具可讀性和可維護(hù)性。
hooks和mixin區(qū)別
相同點(diǎn): hooks和mixin,都是常用代碼邏輯抽離手段,方便進(jìn)行代碼復(fù)用;
異同點(diǎn):
1. 語(yǔ)法和用法不同: Hooks 是在 Vue 3 的 Composition API 中引入的一種函數(shù)式編程的方式,而 Mixins 是在 Vue 2 中的一種對(duì)象混入機(jī)制。Hooks 使用函數(shù)的方式定義和使用,而 Mixins 則是通過(guò)對(duì)象的方式進(jìn)行定義和應(yīng)用。
2. 組合性和靈活性不同: Hooks 允許開發(fā)者根據(jù)邏輯功能來(lái)組合代碼,封裝為自定義Hook 函數(shù),提高代碼復(fù)用率。而 Mixins 在組件中的屬性和方法會(huì)與組件本身的屬性和方法進(jìn)行合并,可能會(huì)導(dǎo)致命名沖突或不可預(yù)料的行為。
3. 響應(yīng)式系統(tǒng)不同: Vue 3 的 Composition API 使用了一個(gè)新的響應(yīng)式系統(tǒng),可以通過(guò) reactive 和 ref 來(lái)創(chuàng)建響應(yīng)式數(shù)據(jù),可以更精確地控制組件的更新和依賴追蹤。而 Mixins 使用 Vue 2 的響應(yīng)式系統(tǒng),對(duì)數(shù)據(jù)的追蹤和更新較為簡(jiǎn)單,可能存在一些性能上的問(wèn)題。
4. 生命周期鉤子不同: 在 Vue 3 的 Composition API 中,可以使用 onMounted、onUpdated 等鉤子函數(shù)來(lái)替代 Vue 2 中的生命周期鉤子,可以更靈活地管理組件的生命周期。Mixins 依然使用 Vue 2 的生命周期鉤子。
mixins 的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):組件中相同代碼邏輯復(fù)用;
缺點(diǎn):
- 變量來(lái)源不明確:變量來(lái)源不明確(隱式傳入),不利于閱讀,使代碼變得難以維護(hù)。
- 命名沖突:多個(gè) mixins 的生命周期會(huì)融合到一起運(yùn)行,但是同名屬性、同名方法無(wú)法融合,可能會(huì)導(dǎo)致沖突。
- 濫用會(huì)造成維護(hù)問(wèn)題:mixins 和組件可能出現(xiàn)多對(duì)多的關(guān)系,復(fù)雜度較高(即一個(gè)組件可以引用多個(gè) mixins,一個(gè) mixins 也可以被多個(gè)組件引用)。
注:VUE3 提出的 Composition API 旨在解決這些問(wèn)題。mixins 的缺點(diǎn)是 Composition API 背后的主要?jiǎng)右蛑?,Composition API 受到 React Hooks 的啟發(fā)。
hooks代碼:
useCount.ts函數(shù)示例:
import { ref, onMounted, computed } from 'vue'; export default function useCount { const count = ref(0); const doubleCount = computed( () => count.value * 2 ); const increase = (delta) => { return count.value + delta; } return { count, doubleCount, increase }; }
useCount在組件中調(diào)用:
<script setup lang="ts"> import useCount from "@/hooks/useCount"; const { count,doubleCount,increase } = useCount; const newCount = increase(10); // 輸出: 10 </script>
Mixins 的代碼:
export default const countMixin = { data() { return { count: 0 }; }, computed: { doubleCount() { return this.count * 2; } }, methods: { increase(delta){ return this.count + delta; } };
Mixins在組件中調(diào)用:
import countMixin from '@/mixin/countMixin'; export default { mixins: [countMixin], mounted() { console.log(this.doubleCount);// 輸出: 0 const newCount = this.setIncrease(10); // 輸出: 10 }, methods: { setIncrease(count){ this.increase(count) } } }
這兩個(gè)示例展示了使用 Hooks 和 Mixins 的代碼風(fēng)格和組織方式的不同。Hooks 使用函數(shù)式的方式來(lái)定義邏輯和狀態(tài),而 Mixins 則是通過(guò)對(duì)象的方式進(jìn)行組合和共享代碼。
Vue3自定義Hooks是組件下的函數(shù)作用域的,而Vue2時(shí)代的Mixins是組件下的全局作用域。全局作用域有時(shí)候是不可控的,就像var和let這些變量聲明關(guān)鍵字一樣,const和let是var的修正。Composition Api正是對(duì)Vue2時(shí)代Option Api 高耦合和隨處可見this的黑盒的修正,Vue3自定義Hooks是一種進(jìn)步。
hooks函數(shù)封裝示例
示例1:數(shù)據(jù)導(dǎo)出(useDownload)
useDownload函數(shù)封裝:
import { ElNotification } from "element-plus"; /** * @description 接收數(shù)據(jù)流生成 blob,創(chuàng)建鏈接,下載文件 * @param {any} data 導(dǎo)出的文件blob數(shù)據(jù) (必傳) * @param {String} tempName 導(dǎo)出的文件名 (必傳) * @param {Boolean} isNotify 是否有導(dǎo)出消息提示 (默認(rèn)為 true) * @param {String} fileType 導(dǎo)出的文件格式 (默認(rèn)為.xlsx) * */ interface useDownloadParam { data: any; tempName: string; isNotify?: boolean; fileType?: string; } export const useDownload = async ({ data, tempName,isNotify = true, fileType = ".xlsx" }: useDownloadParam) => { if (isNotify) { ElNotification({ title: "溫馨提示", message: "如果數(shù)據(jù)龐大會(huì)導(dǎo)致下載緩慢哦,請(qǐng)您耐心等待!", type: "info", duration: 3000 }); } try { const blob = new Blob([data]); // 兼容 edge 不支持 createObjectURL 方法 if ("msSaveOrOpenBlob" in navigator) return window.navigator.msSaveOrOpenBlob(blob, tempName + fileType); const blobUrl = window.URL.createObjectURL(blob); const exportFile = document.createElement("a"); exportFile.style.display = "none"; exportFile.download = `${tempName}${fileType}`; exportFile.href = blobUrl; document.body.appendChild(exportFile); exportFile.click(); // 去除下載對(duì) url 的影響 document.body.removeChild(exportFile); window.URL.revokeObjectURL(blobUrl); } catch (error) { console.log(error); } };
useDownload在組件中使用:
<script setup lang="ts"> import { useDownload } from "@/hooks/useDownload"; const userForm = reactive({}) const userListExport = () => { new Promise(resolve => { $Request({ url: $Urls.userListExport, method: "post", data: userForm, responseType: "blob" }).then((res: any) => { useDownload({ data: res.data, tempName:"用戶列表" }); resolve(res); }); }); }; </script>
示例2:加減計(jì)數(shù)(useCount)
useCount函數(shù)封裝:
import { computed, ref, Ref } from "vue" // 定義hook方法 type CountResultProps = { count:Ref<number>; multiple:Ref<number>; // 計(jì)算屬性 increase:(delta?:number)=>void; decrease:(delta?:number)=> void; } export default function useCount(initValue = 1):CountResultProps{ const count = ref(initValue) const multiple = computed( ()=>count.value * 2 ) const increase = (delta?:number):void =>{ if(typeof delta !== 'undefined'){ count.value += delta }else{ count.value += 1 } } const decrease = (delta?:number):void=>{ if(typeof delta !== "undefined"){ count.value -= delta }else{ count.value -= 1 } } return { count, increase, decrease, multiple } }
useCount函數(shù)在組件中使用:
<template> <p>count:{{count}}</p> <p>倍數(shù):{{multiple}}</p> <div> <button @click="increase(1)">加一</button> <button @click="decrease(1)">減一</button> // 在模版中直接使用hooks中的方法作為回調(diào)函數(shù) </div> </template> <script setup lang="ts"> import useCount from "@/hooks/useCount" const {count,multiple,increase,decrease} = useCount(10) </script>
示例3:獲取鼠標(biāo)觸發(fā)點(diǎn)坐標(biāo)(useMousePosition)
useMousePosition函數(shù)封裝:
import { ref, onMounted, onUnmounted, Ref } from 'vue' interface MousePosition { x: Ref<number>, y: Ref<number> } export default function useMousePosition(): MousePosition { const x = ref(0) const y = ref(0) const updateMouse = (e: MouseEvent) => { x.value = e.pageX y.value = e.pageY } onMounted(() => { document.addEventListener('click', updateMouse) }) onUnmounted(() => { document.removeEventListener('click', updateMouse) }) return { x, y } }
useMousePosition在組件中使用:
<template> <div> <p>X: {{ x }}</p> <p>Y: {{ y }}</p> </div> </template> <script lang="ts"> import useMousePosition from '@/hooks/useMousePosition' const { x, y } = useMousePosition(); </script>
hooks函數(shù)封裝細(xì)節(jié)歸納
1.hooks函數(shù)接收參數(shù)寫法;
寫法1:參數(shù)通過(guò)props接收,先定義參數(shù)類型,內(nèi)部再解構(gòu);
export function commonRequest(params: Axios.AxiosParams) { let { url, method, data, responseType = "json", } = params; }
寫法2:接收傳參對(duì)象,先設(shè)置默認(rèn)值,再定義參數(shù)類型
interface DeprecationParam { from:string; replacement:string; type:string; } export const useDeprecated = ( { from, replacement,type = 'API' }: DeprecationParam, ) => {}
2.解構(gòu)重命名寫法
// setup中 const { list: goodsList, getList: getGoodsList } = useList( axios.get('/url/get/goods') ) const { list: recommendList, getList: getRecommendList } = useList( axios.get('/url/get/recommendGoods') )
3.KeyboardEvent為鼠標(biāo)按鍵類型
export const useEscapeKeydown = (handler: (e: KeyboardEvent) => void) => {}
總結(jié)
Vue2時(shí)代Option Api ,data、methos、watch.....分開寫,這種是碎片化的分散的,代碼一多就容易高耦合,維護(hù)時(shí)來(lái)回切換代碼是繁瑣的!
Vue3時(shí)代Composition Api,通過(guò)利用各種Hooks和自定義Hooks將碎片化的響應(yīng)式變量和方法按功能分塊寫,實(shí)現(xiàn)高內(nèi)聚低耦合。
到此這篇關(guān)于Vue3自定義Hooks函數(shù)的使用詳解的文章就介紹到這了,更多相關(guān)Vue3 Hooks內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue?3?中?vue-router?的?router.resolve?()?API詳解
router.resolve()?就好比是一個(gè)精準(zhǔn)的?“導(dǎo)航參謀”,當(dāng)我們?cè)?Vue?3?應(yīng)用里需要明確某個(gè)路由地址對(duì)應(yīng)的詳細(xì)信息時(shí),它就能派上用場(chǎng),本文給大家介紹Vue?3?中?vue-router?的?router.resolve?()?API,感興趣的朋友一起看看吧2025-04-04Vue項(xiàng)目從webpack3.x升級(jí)webpack4不完全指南
前段時(shí)間,泡面將自己的一個(gè)Vue-cli構(gòu)建的前端框架從webpack3.x升級(jí)到了4.x版本,現(xiàn)在才拉出來(lái)記錄一下,已備忘之用,也和大家分享一下,需要的朋友可以參考下2019-04-04Vue源碼解析之?dāng)?shù)據(jù)響應(yīng)系統(tǒng)的使用
這篇文章主要介紹了Vue源碼解析之?dāng)?shù)據(jù)響應(yīng)系統(tǒng)的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04vue項(xiàng)目中使用lib-flexible解決移動(dòng)端適配的問(wèn)題解決
這篇文章主要介紹了vue項(xiàng)目中使用lib-flexible解決移動(dòng)端適配的問(wèn)題解決,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08Element plus實(shí)現(xiàn)圖片手動(dòng)上傳與回顯的過(guò)程
近期,發(fā)現(xiàn)點(diǎn)擊修改,element ui 的圖片沒有回顯到框中,所以本文給大家介紹了Element plus實(shí)現(xiàn)圖片手動(dòng)上傳與回顯的過(guò)程,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下2024-09-09vue實(shí)現(xiàn)一個(gè)單文件組件的完整過(guò)程記錄
整個(gè)項(xiàng)目結(jié)構(gòu)清晰,尤其單文件組件的表現(xiàn)力尤為突出,使得每個(gè)組件的邏輯都沒有過(guò)于復(fù)雜,所以這篇文章主要給大家介紹了關(guān)于vue實(shí)現(xiàn)一個(gè)單文件組件的相關(guān)資料,需要的朋友可以參考下2021-06-06