Vue3自定義Hooks函數(shù)的使用詳解
hooks是什么
vue3 中的 hooks 就是函數(shù)的一種寫法,就是將文件的一些單獨(dú)功能的js代碼進(jìn)行抽離出來進(jìn)行封裝使用。
它的主要作用是Vue3借鑒了React的一種機(jī)制,用于在函數(shù)組件中共享狀態(tài)邏輯和副作用,從而實(shí)現(xiàn)代碼的可復(fù)用性。
注意:其實(shí) hooks 和 vue2 中的 mixin 有點(diǎn)類似,但是相對 mixins 而言, hooks 更清楚復(fù)用功能代碼的來源, 更清晰易懂。
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ù)名/文件名以use開頭,形如: useXX
3、引用時將響應(yīng)式變量或者方法顯式解構(gòu)暴露出來;
示例如下:
const { nameRef,Fn }= useXX()hooks和utils區(qū)別
相同點(diǎn): 通過hooks和utils函數(shù)封裝, 可以實(shí)現(xiàn)組件間共享和復(fù)用,提高代碼的可重用性和可維護(hù)性。
異同點(diǎn):
1.表現(xiàn)形式不同: hooks是在 utils 的基礎(chǔ)上再包一層組件級別的東西(鉤子函數(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)和生命周期方法提取出來,并在多個組件之間共享和重用;utils通常是指一些輔助函數(shù)或工具方法,用于實(shí)現(xiàn)一些常見的操作或提供特定功能。
總結(jié):
utils是通用的工具函數(shù),而hooks是對utils的一種封裝,用于在組件中共享狀態(tài)邏輯和副作用。
通過使用hooks,您可以簡化代碼,并使其更具可讀性和可維護(hù)性。
hooks和mixin區(qū)別
相同點(diǎn): hooks和mixin,都是常用代碼邏輯抽離手段,方便進(jìn)行代碼復(fù)用;
異同點(diǎn):
1. 語法和用法不同: Hooks 是在 Vue 3 的 Composition API 中引入的一種函數(shù)式編程的方式,而 Mixins 是在 Vue 2 中的一種對象混入機(jī)制。Hooks 使用函數(shù)的方式定義和使用,而 Mixins 則是通過對象的方式進(jìn)行定義和應(yīng)用。
2. 組合性和靈活性不同: Hooks 允許開發(fā)者根據(jù)邏輯功能來組合代碼,封裝為自定義Hook 函數(shù),提高代碼復(fù)用率。而 Mixins 在組件中的屬性和方法會與組件本身的屬性和方法進(jìn)行合并,可能會導(dǎo)致命名沖突或不可預(yù)料的行為。
3. 響應(yīng)式系統(tǒng)不同: Vue 3 的 Composition API 使用了一個新的響應(yīng)式系統(tǒng),可以通過 reactive 和 ref 來創(chuàng)建響應(yīng)式數(shù)據(jù),可以更精確地控制組件的更新和依賴追蹤。而 Mixins 使用 Vue 2 的響應(yīng)式系統(tǒng),對數(shù)據(jù)的追蹤和更新較為簡單,可能存在一些性能上的問題。
4. 生命周期鉤子不同: 在 Vue 3 的 Composition API 中,可以使用 onMounted、onUpdated 等鉤子函數(shù)來替代 Vue 2 中的生命周期鉤子,可以更靈活地管理組件的生命周期。Mixins 依然使用 Vue 2 的生命周期鉤子。
mixins 的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):組件中相同代碼邏輯復(fù)用;
缺點(diǎn):
- 變量來源不明確:變量來源不明確(隱式傳入),不利于閱讀,使代碼變得難以維護(hù)。
- 命名沖突:多個 mixins 的生命周期會融合到一起運(yùn)行,但是同名屬性、同名方法無法融合,可能會導(dǎo)致沖突。
- 濫用會造成維護(hù)問題:mixins 和組件可能出現(xiàn)多對多的關(guān)系,復(fù)雜度較高(即一個組件可以引用多個 mixins,一個 mixins 也可以被多個組件引用)。
注:VUE3 提出的 Composition API 旨在解決這些問題。mixins 的缺點(diǎn)是 Composition API 背后的主要動因之一,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)
}
}
}這兩個示例展示了使用 Hooks 和 Mixins 的代碼風(fēng)格和組織方式的不同。Hooks 使用函數(shù)式的方式來定義邏輯和狀態(tài),而 Mixins 則是通過對象的方式進(jìn)行組合和共享代碼。
Vue3自定義Hooks是組件下的函數(shù)作用域的,而Vue2時代的Mixins是組件下的全局作用域。全局作用域有時候是不可控的,就像var和let這些變量聲明關(guān)鍵字一樣,const和let是var的修正。Composition Api正是對Vue2時代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ù)龐大會導(dǎo)致下載緩慢哦,請您耐心等待!",
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();
// 去除下載對 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ù)通過props接收,先定義參數(shù)類型,內(nèi)部再解構(gòu);
export function commonRequest(params: Axios.AxiosParams) {
let {
url,
method,
data,
responseType = "json",
} = params;
}寫法2:接收傳參對象,先設(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時代Option Api ,data、methos、watch.....分開寫,這種是碎片化的分散的,代碼一多就容易高耦合,維護(hù)時來回切換代碼是繁瑣的!
Vue3時代Composition Api,通過利用各種Hooks和自定義Hooks將碎片化的響應(yīng)式變量和方法按功能分塊寫,實(shí)現(xiàn)高內(nèi)聚低耦合。
到此這篇關(guān)于Vue3自定義Hooks函數(shù)的使用詳解的文章就介紹到這了,更多相關(guān)Vue3 Hooks內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue?3?中?vue-router?的?router.resolve?()?API詳解
router.resolve()?就好比是一個精準(zhǔn)的?“導(dǎo)航參謀”,當(dāng)我們在?Vue?3?應(yīng)用里需要明確某個路由地址對應(yīng)的詳細(xì)信息時,它就能派上用場,本文給大家介紹Vue?3?中?vue-router?的?router.resolve?()?API,感興趣的朋友一起看看吧2025-04-04
Vue項(xiàng)目從webpack3.x升級webpack4不完全指南
前段時間,泡面將自己的一個Vue-cli構(gòu)建的前端框架從webpack3.x升級到了4.x版本,現(xiàn)在才拉出來記錄一下,已備忘之用,也和大家分享一下,需要的朋友可以參考下2019-04-04
Vue源碼解析之?dāng)?shù)據(jù)響應(yīng)系統(tǒng)的使用
這篇文章主要介紹了Vue源碼解析之?dāng)?shù)據(jù)響應(yīng)系統(tǒng)的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
vue項(xiàng)目中使用lib-flexible解決移動端適配的問題解決
這篇文章主要介紹了vue項(xiàng)目中使用lib-flexible解決移動端適配的問題解決,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08
Element plus實(shí)現(xiàn)圖片手動上傳與回顯的過程
近期,發(fā)現(xiàn)點(diǎn)擊修改,element ui 的圖片沒有回顯到框中,所以本文給大家介紹了Element plus實(shí)現(xiàn)圖片手動上傳與回顯的過程,文中通過代碼示例給大家介紹的非常詳細(xì),具有一定的參考價值,需要的朋友可以參考下2024-09-09
vue實(shí)現(xiàn)一個單文件組件的完整過程記錄
整個項(xiàng)目結(jié)構(gòu)清晰,尤其單文件組件的表現(xiàn)力尤為突出,使得每個組件的邏輯都沒有過于復(fù)雜,所以這篇文章主要給大家介紹了關(guān)于vue實(shí)現(xiàn)一個單文件組件的相關(guān)資料,需要的朋友可以參考下2021-06-06

