vue 组件二次封装Ui处理
vue 组件二次封装Ui处理
在Vue开发中,我们常常需要使用UI框架提供的组件。但是UI框架的组件可能并不符合我们的需求,这时候就需要进行二次封装。下面是一些关于Vue组件二次封装Ui处理的技巧:
常规时候咱们使用组件的props、events、slot等属性的传递
子组件代码:
<template>
<div class="my-input">
<el-input></el-input>
</div>
</template>
<script setup>
export default {
props:[]
}
</script>
<style scoped>
.my-input {
transition: 0.3s;
}
.my-input:hover,
.my-input:focus-within {
filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3));
}
</style>
父组件使用:
<my-input v-model="value"></my-input>
如果使用props接收弊端:
- 基本上组件不会只有一两个属性,属性多的话接收的数据量多,需要写大量的无用代码
- 如果存在多级组件嵌套传值,又是重复代码,并且维护性差
$attrs
和$listeners
解决数据穿透问题
因为vue2和vue3有些不同咱分开讲:
vue2的介绍和使用:
上面感觉很难懂:简单来说就是**$attrs
** 接收传递过来的props的值。**$listeners
** 当 inheritAttrs:true 继承除props之外的所有属性;inheritAttrs:false 只继承class属性
vue2的代码:
father.vue 组件:
<template>
<child :name="name" :age="age" :infoObj="infoObj" @updateInfo="updateInfo" @delInfo="TodelInfo" />
</template>
<script>
import Child from '../components/child.vue'
export default {
name: 'father',
components: { Child },
data () {
return {
name: 'zhangyangguang',
age: 24,
infoObj: {
from: '济南',
job: 'policeman',
hobby: ['reading', 'writing', 'skating']
}
}
},
methods: {
updateInfo() {
console.log('update info');
},
TodelInfo() {
console.log('delete info');
}
}
}
</script>
child.vue 组件:
<template>
<son :height="height" :weight="weight" @addInfo="addInfo" v-bind="$attrs" v-on="$listeners" />
// 通过 $listeners 将父作用域中的事件,传入 grandSon 组件,使其可以获取到 father 中的事件
</template>
<script>
import Son from '../components/Son.vue'
export default {
name: 'child',
components: { Son },
props: ['name'],
data() {
return {
height: '183cm',
weight: '76kg'
};
},
created() {
console.log(this.$attrs);
// 结果:age, infoObj, 因为父组件共传来name, age, infoObj三个值,由于name被 props接收了,所以只有age, infoObj属性
console.log(this.$listeners); // updateInfo: f, TodelInfo: f
},
methods: {
addInfo () {
console.log('add info')
}
}
}
</script>
son.vue 组件:
<template>
<div>
{{ $attrs }} --- {{ $listeners }}
<div>
</template>
<script>
export default {
... ...
props: ['weight'],
created() {
console.log(this.$attrs); // age, infoObj, height
console.log(this.$listeners) // updateInfo: f, TodelInfo: f, addInfo: f
this.$emit('updateInfo') // 可以触发 father 组件中的updateInfo函数
}
}
</script>
一般不常用,可读性不是很好。但是组件嵌套层比较深,props很繁琐,可以使用。
vue3的使用和介绍:
代码:
<template>
<div class="my-input">
<el-input v-bind="$attrs"></el-input>
</div>
</template>
<script setup></script>
<style scoped>
.my-input {
transition: 0.3s;
}
.my-input:hover,
.my-input:focus-within {
filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3));
}
</style>
直接 通过**$attrs
** 接收属性和方法。后面在详细介绍vue3的Attributes
这样到这里已经解决属性和方法的问题了。
解决传值和方法问题,还有一个slot插槽
比如elementPlus的input 有如下插槽:
初步解决方法:
封装组件里定义对应数量的插槽然后再次传递
<template>
<div class="my-input">
<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>
<script setup></script>
<style scoped>
.my-input {
transition: 0.3s;
}
.my-input:hover,
.my-input:focus-within {
filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3));
}
</style>
这里定义了四个插槽然后再次传递,确实解决了插槽问题。但是使用的组件不一定会使用全部的插槽。如果使用一个那咱们封装的组件就把其他插槽给传递了过去。这样很不保险,并且也不对
解决思路:父级传递几个插槽,咱就传递几个给子组件,不全部传递
进阶解决方法:使用$slots
也就是说**$slots**可以获取父级组件传递的插槽。
vue3代码:
<template>
<div class="my-input">
<el-input v-bind="$attrs">
<template v-for="(val, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}"></slot>
</template>
</el-input>
</div>
</template>
<script>
export default {
created() {
console.log(this.$slots);
},
};
</script>
<style scoped>
.my-input {
transition: 0.3s;
}
.my-input:hover,
.my-input:focus-within {
filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3));
}
</style>
这样就是父级传递啥那就传递啥插槽。到此解决了插槽问题。
到这里还有个最难的**ref
问题:**
因为ref只能作用于当前无法作用到孙组件
对于ref传递问题vue无法解决。但是咱可以换一个思路。使用ref无非是为了使用孙组件暴露的一些方法。那咱就可以吧孙组件的方法提取到子组件
说白了吧孙组件的方法提取到当前实例
代码:
<template>
<div class="my-input">
<el-input ref="inp" v-bind="$attrs">
<template v-for="(val, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData || {}"></slot>
</template>
</el-input>
</div>
</template>
<script>
export default {
created() {
console.log(this.$slots);
},
mounted() {
console.log(this.$refs.inp);
const entries = Object.entries(this.$refs.inp);
for (const [key, value] of entries) {
this[key] = value;
}
},
};
</script>
<style scoped>
.my-input {
transition: 0.3s;
}
.my-input:hover,
.my-input:focus-within {
filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.3));
}
</style>
这样就完美解决了组件的Ui封装。
以上是一些关于Vue组件二次封装Ui处理的技巧。通过二次封装,我们可以更好地满足我们的需求,提高开发效率。