曾幾何時(shí)我們這樣做
多少次我們打算擴(kuò)展第三方Vue組件的時(shí)候是用這種方式:
// 已知有一個(gè)第三方組件 some-3rd-component
<templete>
<some-3rd-component></some-3rd-component>
</templete>
<script>
...
some codes 寫下我們需要的功能代碼
...
</script>
但是這種方式有重大缺陷
- 第三方組件的事件蝶糯、屬性洋只、方法 等都被隱藏了
- 重新暴露十分麻煩,效率低下,可能需要大量復(fù)制粘貼
- 本質(zhì)是封裝而非擴(kuò)展
有什么辦法可以做到低侵入识虚、適應(yīng)性好肢扯、可以擴(kuò)展功能、又不影響被擴(kuò)展的組件呢担锤?
比如說以下場(chǎng)景
舉個(gè)例子比如已有一個(gè)ElementUi組件: el-select蔚晨,這個(gè)組件有一個(gè)缺陷,無法獲知選中的下拉選項(xiàng)在數(shù)組中的位置肛循。
比如說我有個(gè)數(shù)組
[{name:'廣州',code:'Guangzhou',population:'10'},
{name:'上海',code:'Shanghai',population:'11'},
{name:'北京',code:'Beijing',population:'12'}]
我希望在選擇完城市之后铭腕,獲的選中城市的population
屬性的值,要怎么辦育拨?
至少在成文的這個(gè)時(shí)間里谨履,el-select 還沒有方便的辦法。
introducing vue mixin
minxin 官方文檔的描述是這樣的:
混入 (mixin) 提供了一種非常靈活的方式熬丧,來分發(fā) Vue 組件中的可復(fù)用功能笋粟。一個(gè)混入對(duì)象可以包含任意組件選項(xiàng)。當(dāng)組件使用混入對(duì)象時(shí)析蝴,所有混入對(duì)象的選項(xiàng)將被“混合”進(jìn)入該組件本身的選項(xiàng)害捕。
使用Vuez自帶的mixin在擴(kuò)展組件的時(shí)候十分好用,它避免了前文中的缺陷闷畸,可以恰當(dāng)?shù)貙?shí)現(xiàn)我們所要的功能
我們要做的是尝盼,建立一個(gè)自定義組件,然后把想要擴(kuò)展的組件"mixin"進(jìn)來佑菩。
* 說點(diǎn)題外話盾沫,如果你的ElementUi是全局引入的,那么你也許不能直接通過 import 來引入一個(gè)組件對(duì)象
import Vue from 'vue';
var elSelect = Vue.component('ElSelect'); // 引入要擴(kuò)展的對(duì)象
export default {
name: 'ElSelectEx',
mixins: [elSelect],
watch: {
selected: function (val, oldVal) {
let oldSelected = this.selectEx.selectedIndexes;
let selectedIndex = this.getSelectedIndex(val);
this.selectEx.selectedIndexes = selectedIndex;
if (oldSelected !== undefined && oldSelected !== null) {
// 剛初始化沒有值殿漠,就不發(fā)布更新了
this.$emit('selectedIndexChanged', selectedIndex, oldSelected);
}
},
},
data() {
return {
selectEx: {
selectedIndexes: null,
}
}
},
methods: {
getSelectedIndex(selected) {
if (this.multiple) {
let result = [];
selected.forEach(item => {
result.push(this.options.indexOf(item))
});
return result;
} else {
return this.options.indexOf(selected);
}
}
}
}
使用時(shí)赴精,跟原來的 el-select 沒什么兩樣,但是現(xiàn)在多了一個(gè)事件 selectedIndexChanged
绞幌,當(dāng)選中項(xiàng)變化時(shí)蕾哟,可以在事件參數(shù)中獲得選中項(xiàng)的下標(biāo)
<template>
<div>
<el-select-ex v-model="selected" @selectedIndexChanged="selectIndexChange">
<el-option v-for="item in options" :value="item.code" :label="item.name"></el-option>
</el-select-ex>
</div>
</template>
...
data(){
return{
options:[{name:'廣州',code:'Guangzhou',population:'10'},
{name:'上海',code:'Shanghai',population:'11'},
{name:'北京',code:'Beijing',population:'12'}]
}
}
methods:{
selectIndexChange(val,oldVal){
console.info(this.options[val].population);
}
}
后續(xù)
1 這種擴(kuò)展方式也有一個(gè)缺陷,就是不能自如地修改布局莲蜘,如果硬要改的話谭确,要不重新寫render
要不復(fù)制一份<template>
,還是有點(diǎn)蛋疼
2 使用 extends
可以達(dá)到相同的效果
后續(xù)2
vue 還提供了 $attrs 獲取綁定的屬性
可以通過 v-bind="$attrs"
傳入內(nèi)部組件
以及 $listeners 獲取綁定的事件
可以通過 v-on="$listeners"
傳入內(nèi)部組件
讓創(chuàng)造基于其他組件庫(kù)的組件更加便利