什么是Hook函數
Hook翻譯過來是鉤子的意思,其本質上是一組可復用的函數。簡單理解來說,你能夠在不同的組件中,實現相同的代碼邏輯,以達到代碼復用、提高維護性的效果。那為何叫’鉤子’呢,我的理解是:
它可以通過特定的函數將邏輯 “鉤入” 組件中,使得開發者能夠更靈活地構建和管理組件的功能從而提高代碼的可讀性以及可維護性等
細則:
看完上面的概念,可能還是一頭漿糊,這不就是普普通通封裝了一個函數然后給我們調用嘛,跟我們平時的函數有啥區別?是,這也是我在學習過程中很疑惑的點,來看看下面的解釋吧!
在開發過程中,我們經常會發現一些可重復利用的代碼塊,于是我們將其封裝成函數以供調用。這類函數包括工具函數,但又不止工具函數,因為我們可能也會封裝一些重復的業務邏輯。但在以往,在前端原生開發中,我們所封裝的函數,大多數是"無狀態"的,不能夠建立數據與視圖之間的聯系。
那何為"有狀態"的函數呢?這里的"有狀態",實際上是指是否含有響應式變量。我們知道,基于MVC架構的React框架和基于MVVM的Vue框架都引入了"狀態"這一概念。狀態是特殊的JavaScript變量,它的變化能夠引起視圖的變化。在這類框架中,如果一個變量的變化不會引起視圖變化,則為普通變量,如果一個變量已經被框架注冊為狀態,那么這個變量的變化就會引起視圖變化,我們稱之為響應式變量。
總結即:如果一個函數包含了響應式變量,那么它就是一個Hook函數。
Hook實現原理
看完上面的解釋,我想你應該也能有所領悟,上面提到,所謂Hook函數是指包含了響應式變量,能夠讓組件實現代碼復用的函數。
那在Vue3中,Hooks是基于Composition API實現的其實也不難理解,因為它需要基于響應式變量嘛!
Hooks通過Vue3中setup函數來使用的,setup函數是Vue3組件中的一個新的生命周期函數,它在組件實例被創建之前調用,并且接收兩個參數:props和context。在setup函數中,我們可以定義和返回組件中需要使用的響應式數據、方法、計算屬性等,而這些都可以通過Hooks來實現。
Hook簡單實現
知道了它是怎么個事?那我們怎么用呢!來和我一起探討吧!
因為本篇文章更多的是想讓大家明白hook函數,所以我們這里就舉一個特別簡單的例子!
業務需求:追蹤鼠標位置
在一個夜黑風高的晚上,卷圣優哉游哉地喝著它的咖啡?,此時它的后端小老弟給它發來信息,說,“大哥!不好了!這老板又改需求了!🤯說需要渲染出用戶鼠標的實時位置,而且幾乎每個頁面都要!!你可能需要加一下班啊!”這時,可能普通程序員已經慌了,但作為一個資深大佬,我們已經很有經驗了!
卷圣心想:我就簡單地寫個hook函數去追蹤鼠標位置,然后再到不同視圖組件里面去調用,再渲染一下不就成了…,卷圣回復說好的,明白了。
業務實現
沒用hook前:
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'const x = ref(0)
const y = ref(0)function update(event) {x.value = event.pageXy.value = event.pageY
}onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
</script><template>Mouse position is at: {{ x }}, {{ y }}</template>
這時,如果多個頁面都同時需要的話,你復制這段代碼跑來跑去,而且,你要知道的是,每個頁面中的onMounted跟onUnmounted都不一定是空的,并且,變量的命名上,都有可能出現重復的情況發生。這在我們后期維護起來,是非常不方便的!那我們來看看帥氣的hook方法吧!
用hook后:
主頁面
<script setup>
import { useMouse } from './mouse.js'const { x, y } = useMouse()
</script><template>Mouse position is at: {{ x }}, {{ y }}</template>
在同一目錄下新建mouse.js文件,在文件內寫入以下內容
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'// 按照慣例,組合式函數名以“use”開頭
export function useMouse() {// 被組合式函數封裝和管理的狀態const x = ref(0)const y = ref(0)// 組合式函數可以隨時更改其狀態。function update(event) {x.value = event.pageXy.value = event.pageY}// 一個組合式函數也可以掛靠在所屬組件的生命周期上// 來啟動和卸載副作用onMounted(() => window.addEventListener('mousemove', update))onUnmounted(() => window.removeEventListener('mousemove', update))// 通過返回值暴露所管理的狀態return { x, y }
}
在這里,我們封裝了一個hook庫來實現鼠標追蹤,若有其他組件需要使用,我們只需要將其引入,并使用變量即可,并不需要過多考慮變量重名,生命周期鉤子代碼過多問題!!當然,這里的邏輯比較清晰,當邏輯比較復雜的時候,你可能更能感受到它的強大!
這里介紹一個VueUse 庫,它是Vue3生態中最受歡迎的第三方Hooks庫之一,這里封裝了很多hook函數,或許你能在其中找到或學習更為復雜的hook函數調用!
Hook的使用場景
邏輯復用:當多個組件需要共享相同的邏輯時,我們可以將這些邏輯封裝成一個Hook,然后在需要的組件中導入并使用它。這樣可以避免代碼重復,提高代碼的復用性。
邏輯拆分:對于復雜的組件,我們可以使用Hooks將組件的邏輯拆分成多個獨立的函數,每個函數負責處理一部分邏輯。這樣可以使組件的代碼更加清晰、易于維護。
副作用管理:Hooks中的函數可以訪問組件的響應式數據,并且可以在組件的生命周期中執行副作用操作(如定時器、事件監聽等)。通過使用Hooks,我們可以更好地管理這些副作用操作,確保它們在組件卸載時得到正確的清理。
Vue3中的Hook與Vue2中的mixn
🐼可能你也感受到了,它有點像我們以前 Vue2 學習的mixn!
🐼我們都知道 Vue3 引入 Composition API的寫法,當我們引入一個 hooks 函數的時候其實就像在 Vue2 中使用一個 mixin 一樣,hooks 函數中的ref,reactive就相當于 mixin 中的data,同時 hooks 還可以引入一些生命周期函數,watch 等在 mixin 中都有體現。下面簡單展示一下 mixin 的寫法,不過多講解
定義
export const mixins = {data() {return {msg: "",};},computed: {},created() {console.log("我是mixin中的created生命周期函數");},mounted() {console.log("我是mixin中的mounted生命周期函數");},methods: {clickMe() {console.log("我是mixin中的點擊事件");},},
};
組件中使用
export default {name: "App",mixins: [mixins],components: {},created() {console.log("組件調用minxi數據", this.msg);},mounted() {console.log("我是組件的mounted生命周期函數");},
};
用過 vue2 的 mixin 的都知道,它雖然可以封裝一些邏輯,但是它同時也帶來了一些問題。
比如你引入多個 mixin 它們的 data,methods 命名可能會沖突,當 mixin 多了可能會出現維護性問題,另外 mixin 不是一個函數,因此不能傳遞參數來改變它的邏輯,具有一定的局限性等,但這些問題到了 vue3 的 hooks 中則迎刃而解
Hook優缺點
優點:
提高了代碼的復用性和可維護性。
使組件的邏輯更加清晰、易于理解。
更好地管理組件的副作用操作。
缺點:
學習曲線較陡峭,需要熟悉新的編程模式和思維方式。
對于小型項目或簡單組件,使用Hooks可能過于復雜。
在Vue生態中,第三方Hooks的質量和兼容性可能存在差異。
Hooks書寫規范
命名規范:自定義Hooks應該以“use”為前綴,以區分其他函數和變量。例如:useUserInfo、useMousePosition等。同時,命名應清晰明了,準確描述Hooks的功能。
參數與返回值:自定義Hooks應該接收明確的參數,并返回需要在組件中使用的響應式數據、方法、計算屬性等。返回的對象應該具有清晰的屬性名和結構。
副作用管理:如果自定義Hooks包含副作用操作(如定時器、事件監聽等),應確保在組件卸載時正確清理這些副作用。可以使用onMounted、onUnmounted等生命周期鉤子來管理副作用的添加和移除。
文檔注釋:為自定義Hooks編寫清晰的文檔注釋是非常重要的,說明其用途、參數、返回值和使用示例。這將有助于其他開發者理解和使用你的自定義Hooks。
類型定義(如果使用TypeScript):為自定義Hooks提供類型定義可以確保更好的類型安全性和編輯器支持。使用TypeScript的泛型功能可以增加Hooks的靈活性和可復用性。
測試:為自定義Hooks編寫單元測試是確保其正確性和穩定性的重要手段。測試應該覆蓋各種使用場景和邊界情況。