Vue3二次封装UI组件3板斧

作为一个前端开发,精通Vue、React、Node.js等单词书写,职业Ctrl+CCtrl+V使用者,怎么能不会UI组件的二次封装!下面我将详细的介绍二次组件开发,请各位准备好键盘的 Ctrl+CCtrl+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板斧已会,赶紧自己在项目上秀几手吧!

相关推荐
腾讯TNTWeb前端团队6 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰9 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪9 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy10 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom11 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom11 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom11 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom11 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom11 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试