组件二次封装技巧
不应该在组件中一个一个定义props
$attrs:组件实例的该属性包含了父作用域中不作为prop被识别(且获取)的attribute绑定(class和style除外),当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v-bind=”$attrs”传入内部的UI库组件中。
如何继承第三方组件的属性:使用$attrs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <template> <div class="t_select"> <el-select v-model="childSelectedValue" :style="{width: width||'100%'}" v-bind="attrs" > <el-option v-for="(item,index) in optionSource" :key="index+'i'" :label="item[labelKey]" :value="item[valueKey]" ></el-option> </el-select> </div> </template> <script> export default { name: 'TSelect', computed: { attrs() { return { // 'popper-append-to-body': false, clearable: true, // 默认开启清空 filterable: true, // 默认开启过滤 ...this.$attrs } } } </script>
|
不应该在组件中一个一个的$emit
$listeners:组件实例的该属性包含了父作用域中的(不含.native修饰器的)v-on事件监听器,它可以通过v-on=”listeners”转发传入内部组件,进行对事件的监听处理。
如何继承第三方组件的Event事件:使用$listeners
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <template> <div class="t_select"> <el-select v-model="childSelectedValue" :style="{width: width||'100%'}" v-bind="attrs" v-on="$listeners" > <el-option v-for="(item,index) in optionSource" :key="index+'i'" :label="item[labelKey]" :value="item[valueKey]" ></el-option> </el-select> </div> </template>
|
插槽Slot
slot会在组件模板中占好位置,当使用组件标签的时候,组件标签里面的内容就会自动替换组件模板中slot的位置,并且可以作为承载分发内容的出口。
编译作用域(父组件在子组件<slot></slot>处插入内容)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
<template> <div class= 'app'> <ebutton> {{ parent }}</ebutton> </div> </template>
new Vue({ el:'.app', data:{ parent:'父组件' } })
|
使用数据的语法完全没有变,但是,我们能否直接使用子组件内的数据呢?显然不行!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <template> <div class= 'button'> <button> </button> <slot></slot> </div> </template>
new Vue({ el:'.button', data:{ child:'子组件' } })
<template> <div class= 'app'> <ebutton> {{ child }}</ebutton> </div> </template>
|
直接传入子组件内的数据是不可以的。因为:父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
后备内容 (子组件<slot> </slot>设置默认值)
所谓的后备内容,其实就是slot的默认值,有时我没有在父组件内添加内容,那么 slot就会显示默认值,如:
1 2 3 4 5 6 7
| <template> <div class= 'button'> <button> </button> <slot> 这就是默认值 </slot> </div> </template>
|
具名插槽 (子组件 多个<slot ></slot> <slot></slot> 对应插入内容)
有时候,也许子组件内的slot不止一个,那么我们如何在父组件中,精确的在想要的位置,插入对应的内容呢? 给插槽命一个名即可,即添加name属性。
1 2 3 4 5 6 7 8 9
| <template> <div class= 'button'> <button> </button> <slot name= 'one'> 这就是默认值1</slot> <slot name='two'> 这就是默认值2 </slot> <slot name='three'> 这就是默认值3 </slot> </div> </template>
|
父组件通过v-slot : name 的方式添加内容:
1 2 3 4 5 6 7 8 9 10
| <template> <div class= 'app'> <ebutton> <template v-slot:one> 这是插入到one插槽的内容 </template> <template v-slot:two> 这是插入到two插槽的内容 </template> <template v-slot:three> 这是插入到three插槽的内容 </template> </ebutton> </div> </template>
|
当然 vue 为了方便,书写 v-slot:one 的形式时,可以简写为 #one
作用域插槽 ( 父组件 在子组件 <slot> </slot> 处使用子组件 data)
通过slot 我们可以在父组件为子组件添加内容,通过给slot命名的方式,我们可以添加不止一个位置的内容。但是我们添加的数据都是父组件内的。上面我们说过不能直接使用子组件内的数据,但是我们是否有其他的方法,让我们能够使用子组件的数据呢? 其实我们也可以使用v-slot的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <template> <div class= 'button'> <button> </button> <slot name= 'one' :value1='child1'> 这就是默认值1</slot> //绑定child1的数据 <slot :value2='child2'> 这就是默认值2 </slot> //绑定child2的数据,这里我没有命名slot </div> </template>
new Vue({ el:'.button', data:{ child1:'数据1', child2:'数据2' } })
<template> <div class= 'app'> <ebutton>
// 通过v-slot的语法 将插槽 one 的值赋值给slotonevalue <template v-slot:one = 'slotonevalue'> {{ slotonevalue.value1 }} </template>
// 同上,由于子组件没有给slot命名,默认值就为default <template v-slot:default = 'slottwovalue'> {{ slottwovalue.value2 }} </template>
</ebutton> </div> </template>
|
总结来说就是:
不应该在组件中一个一个的通过this.$refs去获取原组件然后手动添加方法
假设基于el-table进行二次封装:
应该根据this.$refs获取原组件来遍历其所有的方法将其挂载在二次封装的组件上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| mounted() { this.extendMethod() }, methods: { extendMethod() { const refMethod = Object.entries(this.$refs['el-table']) for (const [key, value] of refMethod) { if (!(key.includes('$') || key.includes('_'))) { this[key] = value } } }, }
|