vue元件之路之menu導航選單

火貓裸輝耀發表於2019-03-22

效果儘量仿著element做。 預覽地址

元件之間的通訊

vue元件之路之menu導航選單

 <x-menu :selected.sync="selected"
                    >
        <x-sub-menu name="extension">
          <template slot="title">擴充套件</template>
          <x-menu-item name="mac">for Mac</x-menu-item>
           <x-menu-item name="windows">for Windows</x-menu-item>
             </x-sub-menu>
        <x-sub-menu name="learn">
          <template slot="title">如何使用</template>
            <x-menu-item name="fast">快速入門</x-menu-item>
            <x-menu-item name="advanced">進階配置</x-menu-item>
            <x-menu-item name="package">多語言支援</x-menu-item>
                         
        
 </x-menu>
複製程式碼

selected的傳遞

如圖所示,關於(selected:被選中的那個item),在menu裡面控制,通過watchChild“監聽每個item”,一旦menu-item被點選便會觸發

this.$emit('menuItemUpdate',this.name)
複製程式碼

這裡menu便會通過menu-item$emit$on實現資料的傳遞

watchChild(){
                this.items.forEach(vm=>{
                    vm.$on('menuItemUpdate',name=>{
                        this.$emit('update:selected',[name])
                    })
                })
            },
複製程式碼

這裡的this.items就是收集的每一個menu-item,這在一開始就已經完成了。 要用到依賴注入,這裡面子元件menu-item都直接操作menudata,耦合度非常高。

//menu
 provide(){
          return {
              root:this
          }
        }
複製程式碼
//menu-item
 inject:['root'],
        created(){
           this.root.addItem(this)
          //.........
        },
複製程式碼

然後告訴menu-item你可以被選中了。updated鉤子函式用作完成這個任務再適合不過了

 updated(){
           this.updateChild()
        },
 methods:{
     updateChild(){
              this.items.forEach(vm=>{
                  if(this.selected.indexOf(vm.name)>-1){
                      vm.selected = true
                  }else{
                      vm.selected = false
                  }
              })
          }
 }        
複製程式碼

路徑元素的收集

menu-item被選中時,觸發tellParents函式,收集這條路徑上所有父元素的name,把這個收集好的資料放到selectedArr裡面。這個還可以用來高亮路徑上的父元素。 在click的時候觸發onClick函式

onClick(){
                //...........
                let subFather = this.$parent.$el.classList.contains('x-sub-menu') this.$parent.$el.classList.contains('x-sub-menu')
                let groupFather = this.$parent.$el.classList.contains('x-menu-item-group')
               //..........
                if(subFather||groupFather){
                 //.......
                    this.tellParents(this)
                   //.......
                }else{
                    //.......
                }

            },
複製程式碼

tellParents遞迴呼叫自身來收集 name



 tellParents(that){
                if(that.$parent.$parent.$options.name==='x-sub-menu' ||that.$parent.$parent.$options.name==='x-menu-item-group'){
                    this.root.selectedArr.unshift(that.$parent.name)
                    this.tellParents(that.$parent)
                }else{
                    this.root.selectedArr.unshift(that.$parent.name)
                    console.log(this.root.selectedArr)
                }
            },
複製程式碼

最後就是我希望在menu-item被選中後,可以關閉路徑的所有彈出框popover。 這同樣需要一個遞迴遍歷

childClosePopover(){
                if(this.$parent.$options.name==='x-sub-menu'){
                    this.open = false
                    this.$parent.childClosePopover()
                }
            },
複製程式碼

自定義主題顏色的實現

參照了一下Menu Attribute。 這裡的文字顏色和圖示顏色是同步的, hover的css效果和active的一樣

  • text-color文字顏色 。
  • active-color被選中顏色。
  • back-ground-color正常背景色 。
  • active-back-ground-color被選中的背景色 。

資料傳遞

如圖所示的紫色的路線,通過遞迴遍歷通知menu樹裡面的每一個分支,改變顏色,是否垂直,disabled等等。 大部分新增的功能都在這條線上實現通訊。

js控制顏色的更改

watch:{
            selected(){
                if(this.selected&&........{
                    this.$refs.item.style.backgroundColor = ........
                    this.$refs.item.style.color = .........
                    //...........
                }else{
                  //............
                }
            }
        }
複製程式碼

彈出框的問題

element對彈出框的操作是以在body上新增子節點的方式。這樣子最直接的就是避免了可能存在overflow:hideen的問題。 後面的定位只需要用js來完成就行了,還要考慮到scroll的問題。

vue元件之路之menu導航選單

vue元件之路之menu導航選單

最麻煩的地方在於動畫過渡上,單純的用css會有彈出框回到原點的問題。 這裡就需要用到 javascript鉤子。 官網在這上面的說的很明確,但是實現過程中也踩了一些坑

  • 動畫瞬間完成的問題,這在官網上的解釋時只用JavaScript做過渡的時候(沒用css)的情況下動畫會瞬間完成,要使用done。之後試了並沒有用。這裡並不是只用JavaScript做過渡的。之後想了一下,既然beforeEnterenter瞬間就執行了,並沒有動畫的事件間隔,為什麼不自己加一個 settimeout呢,問題就解決了,可能這種解決方法並不是很好。這裡不用$nextTick原因
  enter(el) {
    setTimeout(()=>{
      //.....
      })
    },
複製程式碼
  • 鉤子中設定的一些額外的css屬性要在afterEnter改回來啊,其中就例如overflow:hiddenheight,導致在vertical垂直皮膚上彈出框不會撐開父元素的問題。
 afterLeave(el){
              el.style.overflow = ''
              if(this.vertical){
                  el.style.height=''
              }
          },
複製程式碼

後面就是和定位彈出框一樣類似的js操作,在menu導航選單裡面我並沒有這麼做,後面會改成這樣的吧。

 beforeEnter(el) {
                el.style.position= 'absolute'
                el.style.transform=.......
                //.........
          },
  
   enter(el) {
             //......
          },
複製程式碼

處理一些細枝末節的問題

  • hover觸發和click觸發
  • 彈出框移出消失的時間控制和動畫抖動。
  • 高亮線條只在一級子元件中存在
  • menu-item-group元件的新增,其實只是作為一箇中轉站而已,無非是拷貝一些函式。
  • 一些多層巢狀的位置問題,尚未完成。

遇到的一些css問題

  • 橫版裡面active下面的亮條顯示,一開始就是設定menu-item裡面的border-bottom,因為同時設定了transition。在顯示的時候會有高度抖動的問題。後來改用偽類完成,不過在自定義顏色的時候非常麻煩。這個方法也放棄了。最後只有在下面加一個div代替border-bottom作為高亮線條,方法雖然很蠢,但是有效。
  • scoped裡面給某些子元件新增css樣式用到了 /deep/深度作用。

簡單的總結?

其實以這種顯而易見的資料流作為基礎,多增加一些功能無非是函式的新增和css的修改。找bug和維護也是相對比較輕鬆的。最後您如果覺得還不錯的話,給我的專案一個star想必也是極好的。

相關文章