元件之間的通訊
<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
都直接操作menu
的data
,耦合度非常高。
//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
的問題。
最麻煩的地方在於動畫過渡
上,單純的用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
元件的新增,其實只是作為一箇中轉站而已,無非是拷貝一些函式。- 一些多層巢狀的位置問題,尚未完成。
遇到的一些css問題
- 橫版裡面
active
下面的亮條顯示,一開始就是設定menu-item
裡面的border-bottom
,因為同時設定了transition
。在顯示的時候會有高度抖動的問題。後來改用偽類完成,不過在自定義顏色的時候非常麻煩。這個方法也放棄了。最後只有在下面加一個div
代替border-bottom
作為高亮線條,方法雖然很蠢,但是有效。 scoped
裡面給某些子元件新增css樣式用到了/deep/
深度作用。
簡單的總結?
其實以這種顯而易見的資料流作為基礎,多增加一些功能無非是函式的新增和css的修改。找bug和維護也是相對比較輕鬆的。最後您如果覺得還不錯的話,給我的專案一個star想必也是極好的。