Vue 3的UI组件库二次封装技巧

背景

在Vue开发过程中,对UI组件库进行二次封装可以极大地提高代码复用性,从而更好地满足项目需求。在组件库的封装过程中,我们通常需要关注四个方面:属性、事件、插槽和方法。下面我们以对ElementPlus的Input组件进行二次封装为例进行说明。

属性、事件

封装MyInput组件,添加一个名为box-shadow的属性,该属性可以用于设置组件的阴影效果。

javascript 复制代码
<!-- MyInput.vue -->
<template>
  <el-input :class="{box_shadow: boxShadow}"> </el-input>
</template>

<script setup>
defineProps({
  boxShadow: {
    type: Boolean,
    default: false,
  },
});
</script>

<style scoped>
.box_shadow {
  box-shadow: 4px 4px 10px #40a0ff7b;
}
</style>

使用v-bind="$attrs"

在 el-input 组件中,存在许多可选的属性和事件。在封装组件时,逐个定义这些属性和事件是不现实的。为了更便捷地处理这种情况,我们可以使用 v-bind="$attrs"。该指令允许将父组件传递给子组件的非props属性绑定到子组件内部的元素上,从而实现更灵活的数据传递和绑定。

例如:

javascript 复制代码
<template>
 <MyInput
     placeholder="请输入关键内容"
     v-model="value"
     @input="inputValue"
   >
 </MyInput>
</template>

<script setup>
import { ref } from "vue";
import MyInput from "./components/MyInput.vue";

const value = ref("123");

const inputValue = (v) => {
  console.log(v);
};
</script>
javascript 复制代码
<!-- MyInput.vue -->
<template>
  <el-input v-bind="$attrs" :class="{box_shadow: boxShadow}"> </el-input>
</template>

<script setup>
defineProps({
  boxShadow: {
    type: Boolean,
    default: false,
  },
});
</script>

<style scoped>
.box_shadow {
  box-shadow: 4px 4px 10px #40a0ff7b;
}
</style>

在这个例子中,父组件中的 placeholder、v-model属性和 input事件并没有在 MyInput 的props中进行显式声明。然而,通过使用 v-bind="$attrs",我们能够将这些属性和事件传递给 el-input 元素。

上面代码等同与:

javascript 复制代码
<!-- MyInput.vue -->
<template>
  <el-input 
      v-model="value"
      :placehoder="placehoder"
      :class="{box_shadow: boxShadow}"
      @input="$emit('input', $event)"
      > 
  </el-input>
</template>

<script setup>
defineProps({
  boxShadow: {
    type: Boolean,
    default: false,
  },
});
</script>

<style scoped>
.box_shadow {
  box-shadow: 4px 4px 10px #40a0ff7b;
}
</style>

插槽

在el-input组件中,同样存在许多可选的插槽。逐个去定义这些插槽是不现实的,因此我们可以使用useSlots方法来获取父组件传入的插槽内容,并通过遍历这些插槽来支持它们的使用。

javascript 复制代码
<template>
  <!-- MyInput.vue -->
  <el-input v-bind="$attrs" :class="{ box_shadow: boxShadow }">
    <template v-for="(value, name) in slots" #[name]="scope">
      <slot :name="name" v-bind="scope || {}"></slot>
    </template>
  </el-input>
</template>

<script setup>
import { onMounted, ref, useSlots, defineExpose } from "vue";

defineProps({
  boxShadow: {
    type: Boolean,
    default: false,
  },
});

const slots = useSlots();
</script>

<style scoped>
.box_shadow {
  box-shadow: 4px 4px 10px #40a0ff7b;
}
</style>

使用

javascript 复制代码
<template>
  <MyInput
    placeholder="请输入关键内容"
    v-model="value"
    @input="inputValue"
  >
    <template #prepend>Http://</template>
  </MyInput>
</template>

<script setup>
import { ref } from "vue";
import MyInput from "./components/MyInput.vue";

const value = ref("123");

const inputValue = (v) => {
  console.log(v);
};
</script>

方法

在el-input组件中,暴露了许多方法。逐个去定义这些方法是不现实的。因此,我们可以通过 ref 来获取el-input组件的实例,然后定义一个expose变量,通过遍历ref获取的方法,将它们存放到expose变量中。接着,我们可以使用defineExpose将expose暴露出来,这样父组件就能通过ref使用这些方法了。

javascript 复制代码
<template>
  <!-- MyInput.vue -->
  <el-input v-bind="$attrs" ref="elInputRef" :class="{ box_shadow: boxShadow }">
    <!-- // -->
  </el-input>
</template>

<script setup>
import { onMounted, ref, useSlots, defineExpose } from "vue";

// ...

const expose = {};

onMounted(() => {
  const entries = Object.entries(elInputRef.value);
  for (const [method, fn] of entries) {
    expose[method] = fn;
  }
});
defineExpose(expose);
</script>

<style scoped>
/* ... */
</style>

使用

javascript 复制代码
<template>
  <MyInput
    placeholder="请输入关键内容"
    v-model="value"
    @input="inputValue"
    ref="myInputRef"
  >
    <template #prepend>Http://</template>
  </MyInput>
</template>

<script setup>
import { onMounted, ref } from "vue";
import MyInput from "./components/MyInput.vue";

const value = ref("123");

const myInputRef = ref();

const inputValue = (v) => {
  console.log(v);
};

onMounted(() => {
  myInputRef.value.focus();
});
</script>

<style>
/* ... */
</style>
相关推荐
行者-全栈开发1 小时前
【前端/JS框架】CVE-2026-3854:GitHub Enterprise Server X-Stat注入XSS漏洞深度剖析与紧急修复指南
前端·javascript·github
计算机魔术师2 小时前
AMD砸82亿买下李飞飞,这次不卖芯片卖什么?
前端
hzxpaipai3 小时前
杭州企业官网怎么建设?从策划到上线的5步建站方法
运维·服务器·前端·网络
怕浪猫3 小时前
2026年金九银十,我面了22个前端
前端·javascript·面试
IT_陈寒3 小时前
Vue 这个响应式陷阱,我的头发都掉没了
前端·人工智能·后端
FYKJ_20103 小时前
django电影推荐系统55066-计算机课程设计、毕业设计
vue.js·spring boot·python·mysql·typescript·spark·django
前端的日常3 小时前
一个人+AI做情侣食谱小程序,30天纯赚
前端·javascript·后端
qq_316837753 小时前
阿里云百炼 fun-asr-realtime uniapp vue3 调用
javascript·vue.js·uni-app
庄园特聘拆椅狂魔4 小时前
Java 后端转全栈的第一课:从前端项目搭建到技术选
java·开发语言·前端
triumph_passion4 小时前
前端路由策略:Memory、Hash 与 History
前端