作为一个前端开发,精通Vue、React、Node.js等单词书写,职业Ctrl+C
、Ctrl+V
使用者,怎么能不会UI组件的二次封装!下面我将详细的介绍二次组件开发,请各位准备好键盘的 Ctrl+C
、Ctrl+V
。
vite创建vue3项目
这里我是使用vite创建的vue3项目,组件库是使用的element-plus,我们自定义的组件为 MyInput
,在App.vue中使用
属性
二次封装UI组件的三板斧,分别是属性、事件、插槽,我们在封装组件的时候,无非是往这个方向,我们就先从属性开始:
可以看出,可传的属性还是很多的,这里我们可以借助$attrs
,接收用户往自定义组件传入的所有属性,$attrs
继承所有的父组件属性(除了 prop 传递的属性、class 和 style )
Vue3中,$attrs改为useAttrs,功能都是一样的
vue
<!-- App.vue -->
<script setup>
import { ref } from 'vue';
import MyInput from "./components/MyInput.vue";
const name = ref('')
</script>
<template>
<MyInput v-model="name" placeholder="自定义组件" clearable size="large" />
</template>
vue
<!-- MyInput.vue -->
<template>
<div>
<el-input v-bind="$attrs"></el-input>
</div>
</template>
<script setup>
import { useAttrs } from "vue";
defineOptions({
name: "MyInput",
});
const $attrs = useAttrs()
</script>
属性透传已经完成,我们在MyInput组件上绑定的属性,通过attrs,全部绑定到el-input上
事件
在我们二次封装UI组件时,还需要使用UI库原本的事件,在Vue2时,我们是通过$listeners
接收父组件传过来的全部事件,然后通过v-on="$listeners"
绑定到el-input上,Vue3中,事件也整合到了useAttrs上了,我们在绑定过属性后,同时的也绑定了事件,实在是太方便了
vue
<!--App.vue -->
<script setup>
import { ref } from 'vue';
import MyInput from "./components/MyInput.vue";
const name = ref('')
const handleInput = ()=>{
console.log('在 Input 值改变时触发')
}
const handleBlur=()=>{
console.log('当选择器的输入框失去焦点时触发')
}
</script>
<template>
<MyInput v-model="name" placeholder="自定义组件" clearable size="large" @input="handleInput" @blur="handleBlur" />
</template>
vue
<!--MyInput.vue -->
<template>
<div>
<el-input v-bind="$attrs"></el-input>
</div>
</template>
<script setup>
import { useAttrs } from "vue";
defineOptions({
name: "MyInput",
});
const $attrs = useAttrs()
console.log($attrs)
</script>
方法透传已经完成,我们在MyInput组件上绑定的事件,通过useAttrs,全部绑定到el-input上
插槽
在我们二次封装UI组件时,还需要使用UI库原本的插槽,在Vue2时,我们是通过$slots
接收所有的插槽,在Vue3中,通过useSlots
,获取父组件传过来的所有插槽
vue
<!--App.vue -->
<script setup>
import { ref } from 'vue';
import MyInput from "./components/MyInput.vue";
const name = ref('')
const handleInput = ()=>{
console.log('在 Input 值改变时触发')
}
const handleBlur=()=>{
console.log('当选择器的输入框失去焦点时触发')
}
</script>
<template>
<MyInput v-model="name" placeholder="自定义组件" clearable size="large" @input="handleInput" @blur="handleBlur" >
<template #prepend>Http://</template>
<template #append>.com//</template>
</MyInput>
</template>
vue
<!--MyInput.vue -->
<template>
<div>
<el-input v-bind="$attrs">
<template #[slotName]="slotProps" v-for="(slot, slotName) in $slots">
<slot :name="slotName" v-bind="slotProps"></slot>
</template>
</el-input>
</div>
</template>
<script setup>
import { useAttrs, useSlots } from "vue";
defineOptions({
name: "MyInput",
});
// 属性和方法
const $attrs = useAttrs();
// 插槽
const $slots = useSlots();
console.log($slots);
</script>
插槽已经完成,我们在MyInput组件上绑定的插槽,通过useSlots,全部传递到el-input上
方法
这是进阶的第四板斧,vue中,我们可以使用ref获取到组件,并且调用组件上的方法,所有UI组件在进行二次封装时,我们也需要获取组件上的方法。我们需要先获取el-iput组件上所有的方法,然后循环挂载到一个对象上,然后将对象暴露出去,父组件就可以直接使用子组件暴露的方法!
vue
<!--App.vue -->
<script setup>
import { ref } from "vue";
import MyInput from "./components/MyInput.vue";
const name = ref("");
const handleInput = () => {
console.log("在 Input 值改变时触发");
};
const handleBlur = () => {
console.log("当选择器的输入框失去焦点时触发");
};
const inputRef = ref()
const clearInput = ()=>{
// console.log(inputRef.value)
inputRef.value.clear()
}
</script>
<template>
<div>
<MyInput
v-model="name"
placeholder="自定义组件"
clearable
size="large"
@input="handleInput"
@blur="handleBlur"
ref="inputRef"
>
<template #prepend>Http://</template>
<template #append>.com//</template>
</MyInput>
<el-button @click="clearInput">清空组件</el-button>
</div>
</template>
vue
<!-- MyInput.vue -->
<template>
<div>
<el-input v-bind="$attrs" ref="inputRef">
<template #[slotName]="slotProps" v-for="(slot, slotName) in $slots">
<slot :name="slotName" v-bind="slotProps"></slot>
</template>
</el-input>
</div>
</template>
<script setup>
import { useAttrs, useSlots, ref, onMounted } from "vue";
defineOptions({
name: "MyInput",
});
// 属性和事件
const $attrs = useAttrs();
// 插槽
const $slots = useSlots();
// console.log($slots);
// 方法 注意这里哦,挂载完才能获取到组件
const inputRef = ref();
const expose = {};
onMounted(() => {
// console.log(inputRef.value)
const entries = Object.entries(inputRef.value);
// 遍历,将方法全部都放在expose上
for (const [method, fn] of entries) {
expose[method] = fn;
}
});
// 暴露expose上的方法
defineExpose(expose);
</script>
3+1板斧已会,赶紧自己在项目上秀几手吧!