二次封装组件
于2023-10-11 09:58:24发布
69
在工作中,有时候我们需要对第三方的组件进行二次封装(比如element-ui),通常我们会遇到以下三个问题 :
1. 属性与事件传值问题
2. 插槽问题
3. ref问题
下面以二次封装element-ui组件为例来解决上面3个问题。
属性与事件传值问题
比如我现在对el-input进行二次封装,组件的名字叫MyInput.vue
<template>
<div>
<el-input></el-input>
</div>
</template>
在App.vue上使用
<template>
<MyInput v-model="val" @blur="blur" placeholder="请输入" clearable></MyInput>
</template>
<script setup>
import { ref } from 'vue'
import MyInput from './components/MyInput.vue'
const val = ref('');
const blur = ()=> {
console.log(val.value);
}
</script>
很明显,这里的属性和事件
<MyInput v-model="val" @blur="blur" placeholder="请输入" clearable></MyInput>
无法传递到MyInput.vue的el-input
解决方法是给MyInput.vue的el-input绑定$attrs属性,如下 :
<template>
<div>
<el-input v-bind="$attrs"></el-input>
</div>
</template>
$attrs可以接收不是props属性的值,比如我给MyInput绑定一个name属性:
<MyInput v-model="val" @blur="blur" placeholder="请输入" clearable :name="'MyInput'"></MyInput>
然后我在MyInput用props接收
<template>
<div>
<el-input v-bind="$attrs"></el-input>
</div>
</template>
<script setup>
import { onMounted, defineProps, getCurrentInstance } from 'vue'
defineProps({
name: {
type: String,
default: "",
},
});
onMounted(() => {
const app = getCurrentInstance();
console.log(app.attrs)
})
</script>
打印结果
$attrs属性里面并没有name属性,因为name属性是props属性
插槽问题
接下来是插槽的问题,element ui的el-input给我们提供了4个插槽,如下 :
现在需要把这些插槽写到MyInput.vue组件去。如下 :
<template>
<div>
<el-input v-bind="$attrs">
<template #prefix>
<slot name="prefix"></slot>
</template>
<template #suffix>
<slot name="suffix"></slot>
</template>
<template #prepend>
<slot name="prepend"></slot>
</template>
<template #append>
<slot name="append"></slot>
</template>
</el-input>
</div>
</template>
使用插槽
<template>
<MyInput v-model="val" @blur="blur" placeholder="请输入" clearable :name="'MyInput'">
<template #prefix>
<div>prefix</div>
</template>
<template #suffix>
<div>suffix</div>
</template>
<template #prepend>
<div>prepend</div>
</template>
<template #append>
<div>append</div>
</template>
</MyInput>
</template>
运行效果
这么写貌似还可以,但是有一个点不是很好,就是我不一定所有的插槽都会用到。我可能只会用其中的一个,但是MyInput.vue里面却把4个插槽都写了,代码有点冗余。
解决的方法是使用$slots属性,$slots属性可以获取到组件的插槽数据(注意,vue的插槽数据实际上是一个Object对象),我们可以打印出来看看
<script setup>
import { getCurrentInstance } from 'vue'
console.log(getCurrentInstance().slots);
</script>
打印结果
能拿到这些数据,我们就可以动态生成slot,如下 :
MyInput.vue
<template>
<div>
<el-input v-bind="$attrs">
<template v-for="(slot, key) in $slots" #[key]>
<slot :name="key"></slot>
</template>
</el-input>
</div>
</template>