Vue官方文檔裏沒吿訴你的神祕鈎子——@hook

語言: CN / TW / HK

theme: cyanosis

最近因為在準備面試而重新温習vue的相關知識點,看到有一些文章介紹了 “如何實現父組件監聽子組件生命週期“ 以下是關於該需求的兩個實現方式的理解

方法一

使用$emit ```javascript // 父組件

// 子組件 ... mounted () { this.$emit('mounted') } updated () { this.$emit('updated') } beforeDestroy () { this.$emit('beforeDestroy') } ... ```

以上的方式可以看出,父組件給子組件傳遞了多個回調函數,依賴子組件在自身的生命週期裏通過vm.$emit的方式,調用父組件生命的回調函數。這樣的方式可以基本滿足我們的需求,但總覺得還是有點笨拙。而且仔細思考的話,不難發現,子組件必須是我們自己編寫的組件,如果子組件是一個第三方庫引用的組件,我們就沒辦法去在它的生命週期裏實現各個回調函數的調用了。

方法二

使用@hook:

```javascript // 父組件

// 子組件

```

看到這裏,可能大家會有點疑惑,在父組件中添加類似@hook:mounted="onMounted"這樣的屬性,子組件無需相關代碼即可實現生命週期監聽嗎?這裏的hook:是什麼東西?

官方文檔中並沒有太多相關的解釋説明,但在程序化的事件偵聽器在這個介紹裏,我們看到了hook的身影。既然子組件無需相關代碼就能觸發父組件通過@hook:綁定的事件,那麼我們猜測這一定是vue自身的邏輯裏實現了這一塊的功能。於是我們嘗試着去源碼裏一探究竟。

image-20210911165340284.png

lifecycle.js文件裏,我們發現vue的生命週期的各個階段都會去調用一個callHook函數,它支持兩個參入,分別是實例vm和對應的生命週期鈎子名稱。而callHook裏面就執行了vm.$emit('hook:' + hook),看到這裏,大家應該就有點熟悉了,這不就是剛才的方法一嗎!所以方法二巧妙的地方就是複用了vue自身的邏輯。

當我們在子組件上傳入了對應的@hook:mounted鈎子,也就是執行了vm.$on('hook:mounted'),而vue實例在生命週期裏本身就會執行vm.$emit('hook:mounted'),其實就連帶着觸發了我們綁定給子組件的回調函數了。這就是@hook:的原理,但是官方文檔在vue2.0裏並沒有將這個api正式地介紹給大家。

拓展

藉助hook:我們還可以進行一些用法的拓展,這些拓展有時候可以提升我們代碼的簡潔性。

在編寫組件時,我們往往需要在各個生命週期裏都針對某個業務邏輯做一些處理,業務散落在各個生命週期鈎子裏:

```javascript

`` 業務邏輯散落在各個生命週期裏,有時候是不利於我們閲讀代碼的,尤其是當該業務是一個複雜的長段代碼時,這個時候我們就可以考慮利用hook:`來梳理某一塊的業務代碼,提升可閲讀性:

```javascript

```

這樣就可以將散落的業務邏輯,都在一個created鈎子函數裏書寫完畢,而且仍保持原來的生命週期邏輯。

此外,另一個使用場景也是比較常見的,例如我們在編寫組件時會執行一些事件監聽或者定時器函數,我們希望在組件銷燬的時候都能去銷燬這些監聽或者定時器,但是由於誇生命週期的問題,常常會將定時器賦值給一個全局變量或者綁定到this上,然後在另一個生命週期裏獲取並執行銷燬操作。

```javascript // 優化前

// 優化後

```

可以看到,經過優化後,我們可以避免了許多data裏的無用變量的定義。