无论是使用element还是antd还是vuetify,大家都喜欢在它们的基础上去封装一些组件,去完成自己想要的效果,我们在封装组件的时候常常会遇到一些可变性,然后就需要我们去修改组件内的东西,以至于组件越改越乱,那么封装组件大家都会,这里我就想说一下我的小tip
$attrs,$listeners这是一个好东西哦 相信大家也都知道并且用过,它们分别可获取到上级中没有在props中声明过的属性和方法,下面我将依次举例来表达我的用法
来看一个基础的,当基于element封装一个按钮Btn.vue
<Btn />即可使用
<el-button
v-bind="$attrs"
v-on="$listeners"
type="primary"
>
按钮
</el-button>
当多层组件嵌套时,一层一层传参太过麻烦,这时可以给层级组件都绑定v-bind="$attrs",即可在最底层组件中使用到最高层组件的属性;其实通过inject,provide也可以实现多层传参,但是也是需要在js代码中定义才能使用,所以总觉得不方便
// one.vue
<div>
这是第一个组件
<two data="hello"/>
</div>
// two.vue
<div>
这是第二个组件
<three v-bind="$attrs"/>
</div>
// three.vue
<div>
这是第三个组件{{$attrs.data}}
</div>
下面还要说一说如何巧用$listeners,这个是可以传递方法的,以下这种做法方法的传递就很清晰了
// one.vue
<div>
这是第一个组件
<two @changeOne="changeThree"/>
</div>
changeThree(val){
console.log(val); // 第三个组件发生改变了
}
// two.vue
<div>
这是第二个组件
<three v-on="$listeners"/>
</div>
// three.vue
<div>
这是第三个组件
</div>
this.$listeners.changeOne('第三个组件发生改变了')
父组件传递一个变量到子组件,子组件绑定事件,这样不需要动用子组件即可给子组件绑定事件,并且把子组件的this放出来,就可以操作子组件内的一切了
// Btn.vue
<el-button
v-bind="$attrs"
v-on="$attrs.listeners(this)"
type="primary"
>
按钮
</el-button>
// Home.vue
<Btn :listeners="listeners"/>
listeners: function(self){
console.log(self)
return {
click:function(data){
console.log(data);
}
}
}
所以我们可以把所有要穿的属性方法统一定义,如下
param:{
getAttrs: ()=>{
return {
clearable:true,
multiple:true
}
},
getListeners: (data)=>{
console.log(data)
return {
change:(value)=>{
console.log(value);
}
}
}
}
// 使用
v-bind="param.getAttrs"
v-on="param.getListeners(_self)"
说完了属性方法,接下来就说一说插槽,个人建议封装的组件都留个插槽出去,方便使用的人自定义
// Btn.vue
<el-button>
<slot></slot>
</el-button>
// Home.vue
<Btn>新增</Btn>
具名插槽
// Btn.vue
<el-button>
<slot name="btnName"></slot>
</el-button>
// Home.vue
<Btn>
<template slot="btnName">
按钮
</template>
</Btn>
作用域插槽
// Btn.vue
<el-button>
<slot name="btnName" :num="num"></slot>
</el-button>
// Home.vue
<Btn>
<template v-slot:btnName="{num}">
按钮{{num}}
</template>
</Btn>
// 当组件内没有传参时你还可以设置默认值
<Btn>
<template v-slot:btnName="{num = 1}">
按钮{{num}}
</template>
</Btn>
// 这样你就不用动组件里的内容就可以在外面自定义展示效果了
<todo-list :todos="todos">
<template v-slot:item="{ item }">
<span v-if="item.select">{{ todo.text }}</span>
</template>
</todo-list>
动态插槽,如下举例可以很大程度的减少template的重复代码
<el-table
:columns="columns"
:data="data"
<template
v-for="item in templateSlot"
:key="item"
v-slot:[item]="scope"
>
{{ scope.scope.row[item] }}
</template>
</el-table>
templateSlot: ['name', 'sex']