記一次Vue跨導航欄問題解決方案

MaxLij發表於2021-07-29

簡述
這篇文章是我專案中,遇到的一個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'
          }
        }
      }
    }

然後發現問題就解決了,如果有明白這個原理的大神,也歡迎在下面留言討論

原始碼地址:https://gitee.com/handsky/vue-nav

相關文章