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裡的無用變數的定義。