前言
前段時間,同事發現一個彈窗元件被多個頁面複用,然後點選按鈕出現了多次響應。明明頁面都會被銷燬掉的呀,為啥還會出現重複呼叫呢。
其實以前我也碰到過一兩次,不知道啥情況後來沒有再復現,再加上工期緊,當時就沒有引起重視。這次同事碰到了,多次試驗復現了。才深知當時挖的大坑啊沒有填。
所以這次就單獨弄了個demo專門復現並解決這個問題。也希望吸取教訓,以後碰到問題都要記錄下來,儘早早早早早解決啊,不然都是坑。
另外,文章裡針對父子元件的全域性事件監聽是錯誤的選擇,請大家不要使用這種。(但是對於兄弟元件的事件響應監聽是可以用全域性的,所以本文適合於這種情況)。
請用官方例子(遇到問題,請多用官方,不然直接參考其他同學的思路很可能挖坑。這裡是以前直接搜尋到eventbus的實現方式,請引以為戒,哈哈,我就做個反面教材吧):
傳送門:cn.vuejs.org/v2/guide/co…
重複響應程式碼示例
首先我寫了一個dialogx的小元件,裡面只有一個點選按鈕。當點選的時候,會觸發一個btnClick
的事件。
然後我又寫了兩個頁面用來測試重複響應。下面是 test1.vue
,至於test2
也一樣,就不重新放圖了。
然後,我們們來點點點點點點!就能發現,在test2
頁面發起的事件,而test1
也響應了,這屬於重複響應是不符合我們的專案需要的。
這...是咋回事~~??
經過查詢相關資料,確定了問題在於沒有過去的銷燬事件響應。
好,我們出發去加上銷燬邏輯。
加入銷燬操作
於是我在dialogx.vue
中加入以下程式碼,在路由切換元件被銷燬的時候,對btnClick
事件進行銷燬。順便列印一下 this.$root
看看裡面是個啥結構。
然後就有了以下的操作,居然...居然 test2.vue 沒法響應了。WTF,這又是啥情況。
冷靜下來,好好分析。首先這種情況是因為我們加了銷燬操作導致的,所以肯定是銷燬的問題。
那麼我們繼續追蹤。發現路由切換的各種操作的順序是這樣的。
這說明 什麼問題?
新頁面的
created
先執行,然後開始逐漸銷燬老頁面。仔細看看,我們能發現問題,test2.vue
在建立監聽事件的時候,是在dialogx.vue
銷燬之前的。人家剛建立好監聽事件,由於執行順序問題,被子元件給清除了,所以test2.vue
在點選發射時,是攔截不到的。
好,既然因為執行順序的問題,那我們把監聽事件註冊放在mounted
裡。
果然,成功了。
再探索一下
對於事件的觸發和監聽,因為我們這是用 eventbus
實現的。為了看看裡面具體是為什麼,於是我列印了 this.$root
看看更細節的是為什麼。
首先,每一次監聽 this.$root.$on('btnClick')
都是一次事件註冊,然後我在列印的結果裡,找到了以下內容。
btnClick
是一個陣列,這就意味著每一次btnClick
事件註冊,都是往裡存一個處理方法。
然後為了驗證我的想法,我把 test2.vue
中的監聽事件去了,直接看我們的 $off
效果。
是 null
,這就解釋了為什麼,剛才監聽事件寫在 created
裡,為什麼不生效了,因為整個事件都被設定為 null
了。
總結
為了達到元件複用不產生重複響應的問題。我們可以如下做。
1.在子元件裡的
destroyed
方法裡,對事件進行銷燬操作($off)
2.在頁面中mounted
方法中進行事件監聽。
GitHub程式碼地址:github.com/XuXiaoGH/vu…
寫在最後
這次是一次異常排查過程的記錄,如果對你有些許幫助,不妨點個喜歡,這將是我繼續的很大動力。
ps: GIF截圖軟體是 LICEcap ,非常好用,推薦一下。