最近在自學 Vue 也瞭解了一些基本用法,也記錄了一些筆記有興趣的朋友可以去檢視我的其他文章,技術這東西真的不能光靠看,看是沒有的,你必須要動手實踐,只有在實戰專案中才能發現問題,才能發現我們沒有掌握的知識點,然後發現問題解決問題,我們的能力才能得以提升,要不然就有點眼高手低了。
基於這個想法於是就開始自己去擼了一個旅遊網站,旅遊網站嘛避免不了城市的選擇,所以在實現城市選擇列表的時候碰到的一些問題,以及解決辦法今天就記錄下來做一個總結。
城市列表選擇元件
首先說說我們要實現一個什麼樣的城市選擇元件:
- 輸入框獲取焦點時,顯示元件
- 點選城市列表更新輸入框的城市顯示
- 點選其他空白處元件隱藏
- 在切換到其他元件時,選擇的城市保留而不是被重置
下面我們就一步一步的來拆解
第一步
輸入框獲取焦點後顯示元件很簡單,我們給輸入框繫結焦點事件然後給元件傳入一個顯示的狀態即可,我們把 isShowCityList 傳遞給城市選擇元件控制行為。
<
el-input @focus="isShowCityList=true" placeholder="請輸入目的地">
<
/el-input>
複製程式碼
第二步
我們也不做過多的表述本文想更多的是介紹動態元件與全域性事件的繫結,利用的是子元件給父元件利用自定義事件 $emit 傳給父元件。
第三步
需要我們去點選其他地方城市元件被隱藏,有些同學的第一印象可能是利用 input 的 blur 事件(就是失去焦點事件),只要我們的 input 失去焦點時,我們就隱藏。
其實我的第一印象也是如此,但是我們繫結的是 input 的失去焦點事件以後,當我們選擇城市列表的時候也是 input 失去焦點的時候,所以我們就無法選取城市。顯然這種思路是不行的。
所以這裡我們只能去用到 Vue 的全域性事件的繫結,然後去進行一個判斷我們點選的節點是哪裡,如果是城市元件以外我們就進行隱藏操作。
我們在 mounted 鉤子函式中,進行如下操作。
mounted() {
document.addEventListener("click", e =>
{
console.log('全域性事件被觸發');
if (!this.$refs.searchCity.contains(e.target)) {
this.isLoadCityList = false;
}
});
}複製程式碼
OK,進行這一步之後,我們的問題得到了解決,只要我們點選這個容器以外的地方就會隱藏城市列表元件,我以為算是結束了,不過那是不可能的,還是我太年輕了,這樣做的後果就是不管我們點選任何一個地方它都會觸發這個事件,即使是我們切換到其他元件時,事件照樣會被觸發,顯然這個不是我們想要的,因為當前事件會被無限觸發,無疑會給我們帶來不可預見的問題。
我們需要的最好效果肯定是當前的全域性事件就在當前的元件下產生作用,當我們切換到其他元件時,事件自動刪除,於是我可能想到的就是利用 beforeDestroy 鉤子函式去刪除這個全域性事件。也就是當我們切換到其他元件時,去刪除這個全域性事件。
beforeDestroy() {
document.removeEventListener("click", () =>
{
//...
});
}複製程式碼
你以為這樣我還就能解決問題了嗎?顯然還是不能,還是太年輕,只是這樣我們是解除不了繫結的事件,那我們該怎麼辦呢?其實這裡面有一個坑,大坑,因為這個大坑自己不知道,查了許多資料也沒查出來,因為查的思路錯了,最後在一個群裡問了一個大佬,才得出答案,不得不說與前輩交流很重要啊,能幫你少踩很多坑。
這裡如果想要解除事件,解除和繫結的兩個回撥函式必須一致,什麼意思呢?看程式碼你就明白。如果不這麼操作,你是解除不掉事件的,至於更深的原因我也不怎麼明白了,以後再去查閱一些資料。
methods: {
isSearchCityNode(e) {
if (!this.$refs.searchCity.contains(e.target)) {
console.log("全域性事件被觸發");
this.isLoadCityList = false;
}
}
},mounted() {
document.addEventListener("click", this.isSearchCityNode);
},beforeDestroy() {
document.removeEventListener("click", this.isSearchCityNode);
}複製程式碼
第四步
需要我們在切換元件的時候保留我們選擇的城市,如果不保留我們每次切換到其他元件時,我們選擇的城市都會被重置為預設值,這個體驗肯定是肯差的,也不是我們想要的。
被重置的原因則是我們在每次在不同的元件進行切換的時候,元件都會進行新建與銷燬,這也會導致重複渲染問題對效能也是不友好的。
那麼我們該如何去處理這個問題呢? 我這裡使用了 keep-alive 去解決這個問題,那麼 keep-alive 該如何使用以及作用是什麼呢?
<
keep-alive>
<
component v-bind:is="currentTabComponent">
<
/component>
<
/keep-alive>
複製程式碼
包裹動態元件時,會快取不活動的元件例項,而不是銷燬它們,它自身不會渲染一個 DOM 元素,也不會出現在父元件鏈中。
但是當我們使用 的時候,我們的 beforeDestroy 鉤子函式就會失效,導致我們第三步的全域性事件的解綁就不能執行了,原因是我們的元件是被快取起來,並沒有被銷燬。自然會失效,但是我們並不慌,當我們使用 keep-alive 時,activated 和 deactivated 兩個鉤子函式被觸發。
activated:keep-alive 元件啟用時呼叫。
deactivated:keep-alive 元件停用時呼叫。
所以我們不難發現,我們完全可以使用這兩個鉤子去實現我們全域性事件的繫結與解綁,簡直完美。
activated() {
document.addEventListener("click", this.isSearchCityNode);
},deactivated() {
document.removeEventListener("click", this.isSearchCityNode);
}複製程式碼
總結
通過一個城市列表元件的案例,介紹了我們在 Vue 中如何繫結全域性事件以及進行優化,一定要記住事件的繫結與解除哪裡有一個大坑。
我們通過 可以建立一個可以快取的元件,而且會新增兩個鉤子函式提供我們使用
文中如有不足之處,歡迎大神拍磚!