1.前言
這次探索源於改造element-ui
中下拉選單的觸發方式,el-dropdown
提供了兩種觸發方式hover
和click
。由於之前專案中有很多自定義右鍵選單的操作。而element-ui
並未提供右鍵選單相關的元件,於是檢視el-dropdown
的原始碼想改造一個支援此右鍵操作的下拉選單。儘管這不符合實際使用者操作習慣哈,不過這只是一次改造element-ui
元件的一個嘗試,改造並非止於此。
2.el-dropdown目錄結構
檢視dropdown.vue
和dropdown-item.vue
中都混入了Emitter
。
import Emitter from 'element-ui/src/mixins/emitter';
3. Emitter
在學習vue自定義事件的時候,父元件在使用子元件時,可以為子元件的自定義事件指定事件處理程式。子元件中可以通過vm.$emit(eventName, [...args])
觸發自定義事件。但是vue官網的案例比較簡單,案例中只涉及到一層子元件。
Emitter
定義了兩個方法,擴充套件了自定義事件,父元件可以通過broadcast
(廣播)的方式觸發後代元件的自定義事件;後代元件可以通過dispatch
(派遣)的方式觸發最近一級指定名稱的祖先元件的自定義事件。原始碼如下:
// 指定後代元件名稱componentName
function broadcast(componentName, eventName, params) {
this.$children.forEach(child => {
var name = child.$options.componentName;
if (name === componentName) {
child.$emit.apply(child, [eventName].concat(params));
} else {
// 遞迴呼叫進行廣播,觸發自定義事件
broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
export default {
methods: {
// 觸發祖先元件的自定義事件
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
// 迴圈找到最近一級名稱為componentName的祖先元件
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
},
broadcast(componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params);
}
}
};
複製程式碼
4.案例
<el-dropdown trigger="click">
<span class="el-dropdown-link">
下拉選單<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>黃金糕</el-dropdown-item>
<el-dropdown-item>獅子頭</el-dropdown-item>
<el-dropdown-item>螺螄粉</el-dropdown-item>
<el-dropdown-item>雙皮奶</el-dropdown-item>
<el-dropdown-item>蚵仔煎</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
複製程式碼
從元件程式碼結果上看,el-dropdown
子元件是el-dropdown-item
,孫元件是el-dropdown-item
。檢視el-dropdown
的偵聽器:
watch: {
visible(val) {
this.broadcast('ElDropdownMenu', 'visible', val);
this.$emit('visible-change', val);
},
focusing(val) {
const selfDefine = this.$el.querySelector('.el-dropdown-selfdefine');
if (selfDefine) { // 自定義
if (val) {
selfDefine.className += ' focusing';
} else {
selfDefine.className = selfDefine.className.replace('focusing', '');
}
}
}
}
複製程式碼
this.broadcast('ElDropdownMenu', 'visible', val);
用於向後代名為ElDropdownMenu
呼叫名為visible
的自定義事件。
在檢視dropdown-item.vue選項中的方法:
methods: {
handleClick(e) {
this.dispatch('ElDropdown', 'menu-item-click', [this.command, this]);
}
}
複製程式碼
this.dispatch('ElDropdown', 'menu-item-click', [this.command, this]);
觸發了el-dropdown
的自定義事件menu-item-click
。
5.總結
Emitter實際上擴充套件了自定義事件的觸發範圍。父元件可以觸發後代元件的自定義事件,不限於兒子元件。後代元件可以觸發最近一級指定名稱的祖先元件的自定義事件。其中涉及到事件處理程式的位置,通俗的講,就是父元件幹了一件事情,它可以通過廣播的方式告訴後代元件作出回應,事件處理程式在後代元件中定義;而後代元件幹了一件事件,可以通知某一祖先元件作出回應,事件處理程式在祖先元件中定義。