【vue3.0】UI组件的二次封装
要实现组件的二次封装,首先得解决三个基本问题
- 属性的传递,即在调用二次封装的组件时,在二次封装组件中传递的属性需要自动传递给原始组件(当然也可以在新封装的组件中使用props来接受并绑定到原始组件上,这样做维护成本太高,代码也不太好阅读)。
- 插槽问题,即在调用二次封装的组件时,在二次封装组件传递的插槽需要自动传递给原始组件(当然也可以在新封装的组件中将原始组件所有插槽全部手写,这样做维护成本太高并且具名插槽会根据有没有传递处理不同的事,但是这样的话对于原始组件来说,所有的插槽都被传递,可能会产生一些莫名其妙的问题,代码也不太好阅读)。
3.ref方法问题,有的组件某些方法是通过ref调用的,例如el-form 的表单重置方法,但是我们经过二次封装的组件ref上是没有原始组件的的方法的。
接下来以此解决这三个问题
-
【$attrs透传】 $attrs透传是指将父组件属性传递给当前组件,排除掉props中声明的属性。
-
【$solts】 可以拿到调用这给当前组件传递的所有插槽。
如//子组件 child.vue <template> <div> {{$attrs}} <!-- 输出{b:'b',c='c'}--> </div> </template> <script setup> const props = defineProps({ a: { type: String, }, }); </script> /*-----------------------------------------------------------------------------------------------*/ //父组件 parent.vue <template> <div> <child a='a' b='b' c='c'></child> </div> </template> <script setup> import child from './child.vue' </script> -
关于ref的解决方案vue官方没有指出。我的思路是使用ref,无非是使用ref对应组件的方法。所以我们只需要拿到元素的组件的所有方法并它绑定在二次封装的组件上即可。
完整代码
my-input.vue
<template> <div> <el-input v-bind="$attrs" ref="inputRef"> <!-- #[name]="slotData 作用域插槽传值--> <template v-for="(val, name) in $slots" #[name]="slotData\"> <slot :name="name" v-bind="slotData||{}"></slot> </template> </el-input> </div> </template> <script> export default { mounted () { let inputRef= this.$refs.inputRef Object.keys(inputRef).forEach(key => { this[key]=inputRef[key] }) } } </script>
index.vue
<template>
<div>
<my-input v-model="inputVal" ref="inputRef" :dir="'aa'" :propsData="{ list: [] }">
<template #prepend>
<el-select v-model="select" placeholder="Select" style="width: 115px">
<el-option label="Restaurant" value="1" />
<el-option label="Order No." value="2" />
<el-option label="Tel" value="3"/>
</el-select>
</template>
<template #append>
<el-button :icon="Search" />
</template>
</my-input>
</div>
</template>
<style></style>
<script setup>
import { ref, reactive, nextTick, onMounted, defineProps, } from 'vue';
import myInput from './my-input.vue'
const inputVal = ref()
const inputRef = ref(null);
onMounted(() => {
inputRef.value.focus();
})
</script>