記一次Vue跨導航欄問題解決方案
- 2021 年 7 月 29 日
- 筆記
- BootstrapVue, default-active, element-ui, vue.js, WEB, 前端
簡述
這篇文章是我項目中,遇到的一個issue,我將解決過程和方法記錄下來。
本篇文章基於Vue.js進行的前端頁面構建,由於僅涉及前端,將不做數據來源及其他部分的敘述。使用的CSS框架是 BootstrapVue 和 Element-UI 。數據使用 json 文件進行模擬,數據可在文章末的鏈接源碼中查看
需求描述
項目需求是實現雙導航欄:頂部導航欄和側邊導航欄。頂部導航欄用於展示一級菜單,根據點擊的不同一級菜單,在螢幕左側展示不同的二級三級導航欄。要求當前頁面導航欄菜單需要高亮。
需求分析
數據,json 文件使用axios進行獲取,然後賦值給Home.vue下的menus變數,再分發到Nav.vue中,進行一級導航的展示。默認情況下,高亮第一個一級菜單。如果該一級菜單下沒有二級菜單和三級菜單,則不顯示左側導航欄,如果有,則展示。當點擊了一級菜單時,默認頁面展示該菜單下的第一個菜單的頁面。
例如:點擊了第一個菜單,這個菜單有二級,三級菜單,則展示該菜單下的三級菜單的頁面,如果沒有三級菜單,則展示二級菜單的頁面,如果沒有二級菜單,則展示一級菜單的頁面。
默認情況下,渲染完成側邊導航欄後高亮導航欄的第一個最下級菜單
主要程式碼結構
遇到的問題
1、父組件與子組件相互傳值
父組件傳值給子組件(Home.vue,父):
父組件傳值個子組件(myNav.vue,子)
子組件通過props進行數據的接收,該屬性是一個數組的形式,父組件通過綁定的形式直接給組件的屬性賦值便可實現父組件向子組件傳值。
子組件傳值給父組件(myNav.vue 子):
子組件傳值給父組件(Home.vue 父):
父組件傳值給子組件與子組件傳值給父組件不同,父組件需要對自定義事件進行監聽,而子組件需要觸發該事件才能夠通過$emit進行傳值,在父組件中,傳值的接收變數名固定為$event,不能使用其他名字進行獲取數據。
2、頂部導航欄的高亮問題
因為頂部導航欄使用的是Bootstrap-Vue框架,所以高亮菜單時只需加上class:active便可以使其高亮,問題是根據需求,高亮的控制權交由父組件Home.vue,而不在子組件myNav.vue中完成,這也就涉及到了剛剛說的組件傳值的問題,上述已經敘述過組件傳值的問題,此處僅簡述其邏輯。
子組件中綁定點擊事件後,會將點擊的一級菜單的ID值傳給父組件。由於原需求中,可能會涉及到URL跳轉到任意頁面,所以,根據this.$route.path來判斷是哪一個頁面,再將這個頁面的ID值傳回給子組件。
傳回後使用三元運算符,來比對是否與渲染時id匹配,若匹配,則高亮
3、左側導航的高亮問題
需求重述:
點擊一級菜單,若該菜單數據中存在二級菜單,則在左側導航欄中顯示,如不存在,則隱藏左側導航欄。默認情況下,若該一級菜單有下屬三級子菜單,則跳轉第一個下屬二級子菜單下的第一個下屬三級菜單;若沒有三級菜單,則跳轉第一個下屬二級子菜單的URL;若沒有下屬二級子菜單,則跳轉至一級的URL。在此基礎上,高亮當前的菜單。
與頂部導航欄不同,頂部導航欄的高亮是依靠菜單的點擊和路由來控制,而側邊導航欄使用的是element-UI的導航欄,根據default-active進行高亮。
default-active的原理是該屬性的值與菜單中的index進行比較,若相等則高亮。原理很簡單,但是實現起來卻沒有那麼容易。
第一次解決方案:在點擊一級菜單時,上述已經說過,將id值傳給父組件Home.vue中。父組件監聽到該事件觸發後,遍歷之前通過axios獲取到的數據,最終找到匹配的一級菜單,進而判斷該菜單下是否有二級乃至三級菜單,若有,則顯示第一個,並且將該屬性的url值賦值給上述的default-active。
從原理的角度來說,該思想沒有問題。可是該方法會導致一個問題,在進入頁面後,點擊任何一級菜單都能夠正常生效,但是互相點擊時,左側導航不顯示高亮,頁面能夠正常跳轉。
例如:進入到該頁面後,第一次點擊任何一級菜單都沒有問題,正常顯示。在第二次點擊任何菜單時均沒有高亮顯示。
在模擬數據中,有一個一級菜單沒有下屬菜單,若第一次點擊除此菜單以外的菜單時,均可正常顯示。第二次若點擊除此菜單以外的菜單時,並不會高亮。
我想說的是,若第二次點擊的是這個沒有下屬菜單的一級菜單,第三次再去點任意菜單,又可以正常顯示了。
對於這個問題,至少在我看來覺得是很神奇的。於是我將這個操作模擬到每一次的頁面點擊中,也就是任何一次點擊都做一次向那個沒有數據的菜單來一次跳轉,再跳轉回來,發現問題解決了。
那麼問題出現在哪裡,應該怎樣解決,總不至於,每次跳轉都需要經過那個沒有下屬菜單的頁面吧,數據是動態的,並不清楚哪個一級菜單沒有下屬頁面菜單。
分析
跳轉至無下屬菜單的一級菜單時,做了兩件事,1:將側邊導航欄數據清空,2:隱藏側邊導航欄。
跳轉至其他菜單時,也做了兩件事,1:重新賦值側邊導航欄,2:顯示側邊導航欄。
首先排除隱藏/顯示導航欄,樣式的修改,應該不至於會出現這種問題。(其實我也偷偷試過。。)
接下來就只剩下賦值和清空數據了
該程式碼如下
click1thMenu (id) {
this.menus2th = []
this.active1thMenu = id
for (let i = 0; i < this.menus.length; i++) {
if (this.menus[i].id === id) {
if (this.menus[i].child !== undefined) {
this.asideWidth = '200px'
this.menus2th = this.menus[i].child
} else {
this.asideWidth = '0'
}
}
}
}
我的程式碼邏輯中,不管是賦值還是情況操作都會將數據清空。到這裡,問題就撲朔迷離了。
好了,不賣關子了,直接說結果
我思考,會不會因為在有下屬菜單的情況下,清空語句執行後再執行賦值語句,會導致情況語句成為無用程式碼,被瀏覽器直接優化掉了,於是我在賦值語句上加了一個延時函數,給他一定的延時。
methods: {
click1thMenu (id) {
this.menus2th = []
this.active1thMenu = id
for (let i = 0; i < this.menus.length; i++) {
if (this.menus[i].id === id) {
if (this.menus[i].child !== undefined) {
this.asideWidth = '200px'
setTimeout(() => {
this.menus2th = this.menus[i].child
}, 10)
} else {
this.asideWidth = '0'
}
}
}
}
然後發現問題就解決了,如果有明白這個原理的大神,也歡迎在下面留言討論
源碼地址://gitee.com/handsky/vue-nav