效果儘可能仿着element作。
預覽地址javascript
<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
:被選中的那個item),在menu
裏面控制,經過watchChild
「監聽每一個item」,一旦menu-item
被點擊便會觸發css
this.$emit('menuItemUpdate',this.name)
這裏menu
便會經過menu-item
的$emit
和$on
實現數據的傳遞html
watchChild(){ this.items.forEach(vm=>{ vm.$on('menuItemUpdate',name=>{ this.$emit('update:selected',[name]) }) }) },
這裏的this.items
就是收集的每個menu-item
,這在一開始就已經完成了。
要用到依賴注入
,這裏面子組件menu-item
都直接操做menu
的data
,耦合度很是高。vue
//menu provide(){ return { root:this } }
//menu-item inject:['root'], created(){ this.root.addItem(this) //......... },
而後告訴menu-item
你能夠被選中了。updated
鉤子函數用做完成這個任務再適合不過了java
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
函數git
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
github
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。
這一樣須要一個遞歸遍歷windows
childClosePopover(){ if(this.$parent.$options.name==='x-sub-menu'){ this.open = false this.$parent.childClosePopover() } },
參照了一下Menu Attribute。
這裏的文字顏色和圖標顏色是同步的,
hover的css效果和active
的同樣ide
active-color被選中顏色。函數
如圖所示的紫色的路線,經過遞歸遍歷通知menu樹
裏面的每個分支,改變顏色,是否垂直,disabled等等。
大部分添加的功能都在這條線上實現通訊。
watch:{ selected(){ if(this.selected&&........{ this.$refs.item.style.backgroundColor = ........ this.$refs.item.style.color = ......... //........... }else{ //............ } } }
element對彈出框的操做是以在body
上添加子節點的方式。這樣子最直接的就是避免了可能存在overflow:hideen
的問題。
後面的定位只須要用js來完成就好了,還要考慮到scroll
的問題。
最麻煩的地方在於動畫過渡
上,單純的用css會有彈出框回到原點的問題。
這裏就須要用到
javascript鉤子。
官網在這上面的說的很明確,可是實現過程當中也踩了一些坑
動畫瞬間完成的問題,這在官網上的解釋時只用JavaScript
作過渡的時候(沒用css)的狀況下動畫會瞬間完成,要使用done。以後試了並無用。這裏並非只用JavaScript作過渡的。以後想了一下,既然beforeEnter
和enter
瞬間就執行了,並無動畫的事件間隔,爲何不本身加一個 settimeout
呢,問題就解決了,可能這種解決方法並非很好。這裏不用$nextTick
的緣由
enter(el) { setTimeout(()=>{ //..... }) },
鉤子中設置的一些額外的css屬性要在afterEnter
改回來啊,其中就例如overflow:hidden
和height
,致使在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
組件的添加,其實只是做爲一箇中轉站而已,無非是拷貝一些函數。active
下面的亮條顯示,一開始就是設置menu-item
裏面的border-bottom
,由於同時設置了transition
。在顯示的時候會有高度抖動的問題。後來改用僞類完成,不過在自定義顏色的時候很是麻煩。這個方法也放棄了。最後只有在下面加一個div
代替border-bottom
做爲高亮線條,方法雖然很蠢,可是有效。scoped
裏面給某些子組件添加css樣式用到了 /deep/
深度做用。其實以這種顯而易見的數據流做爲基礎,多增長一些功能無非是函數的添加和css的修改。找bug和維護也是相對比較輕鬆的。最後您若是以爲還不錯的話,給個人項目一個star想必也是極好的。