UI 组件的二次封装

UI 组件的二次封装是指,在基础 UI 库的组件上进行自定义封装,以实现更贴合业务需求的功能和样式。通过二次封装,可以增强组件的复用性、便捷性和一致性,简化业务代码,同时降低后续维护成本。

1. 二次封装的原理

二次封装基于对原有组件的拓展和定制,一般使用 组合 和 继承 的方式实现。具体实现时,可以利用基础 UI 组件库(如 Element Plus、Vant、Ant Design 等)中的现有组件,通过添加额外的属性、方法或样式,实现业务层面所需的定制功能。

核心思想包括:

1、组合和继承:基于已有组件,通过组合或继承来实现新的组件功能。

2、属性代理:将原有组件的属性透传到封装组件,以保持原组件的功能。

3、事件透传:将用户操作的事件传递给外部调用者,支持外部绑定事件处理。

4、样式定制:通过样式扩展或修改,实现特定样式需求。

5、业务逻辑注入:通过自定义逻辑封装特定的业务逻辑,比如权限控制、数据校验、异步加载等。

2. 原理解释

1、对属性和事件的传递

举个 🌰,对 Element-plus 的 el-input 进行处理。

在 App.vue 中:

html 复制代码
<template>
  <MyInput a="1" b="2" c="3" @change="() => {}" v-model="defaultValue" />
</template>

<script setup>
import MyInput from './components/MyInput.vue'
import { ref } from 'vue'
const defaultValue = ref('default value')
</script>

在 MyInput.vue 中:

html 复制代码
<template>
  <div class="my-input">
    <ElInput />
  </div>
</template>
<script>
import { ElInput } from 'element-plus';
export default {
  components:{
    ElInput
  },
  mounted() {
    console.log(this.$attrs)
  }
}
</script>

使用 $attrs 获取父组件传递的数据,展示为:

但当我们在 自定义组件 内部声明同名属性时:

html 复制代码
<template>
  <div class="my-input">
    <ElInput />
  </div>
</template>
<script>
import { ElInput } from 'element-plus'
export default {
  props: ['a'],
  components: {
    ElInput
  },
  mounted() {
    // this.$attrs 会自动剔除掉声明的属性,也就是在 props 内部定义的
    console.log(this.$attrs)
  }
}
</script>

展示如下:

这个现象是合理的,声明这个属性,说明我们接下来需要使用它做一些额外的处理,对组件本身是有意义的。而没有声明的数据,就汇总到 $attrs 中,可以将这些属性透传给 原始组件 本身。

如下:

html 复制代码
<template>
  <div class="my-input">
    <ElInput v-bind="$attrs"/>
  </div>
</template>
<script>
import { ElInput } from 'element-plus'
export default {
  props: ['a'],
  components: {
    ElInput
  },
  mounted() {
    console.log(this.$attrs)
  }
}
</script>

对此可以实现,将传递过来的、无需额外更改的属性和方法,透传到 UI 库组件本身。

2、对 插槽(slot)进行处理

在 App.vue 中,传递两个插槽内容。

html 复制代码
<template>
  <MyInput>
    <template #prepend>
      <el-button>prepend</el-button>
    </template>
    <template #append>
      <el-button>append</el-button>
    </template>
  </MyInput>
</template>

在 MyInput 组件中:

html 复制代码
<template>
  <div class="my-input">
    <ElInput v-bind="$attrs">
      <!-- 遍历传递给当前组件的所有插槽($slots),并生成每个插槽对应的模板。 -->
      <template v-for="(_, name) in $slots" #[name]="scopeValue">
        <slot :name="name" v-bind="scopeValue"></slot>
      </template>
    </ElInput>
  </div>
</template>

展示如下,父组件将插槽内容传递给子组件。

3、对 ref 的传递(!important)

虽然在某些特殊的场景下需要使用 ref,但是不建议对 ref 进行传递,那如何处理呢?

在父组件中获取子组件的对象,无非是使用其方法和属性,那就将这些属性传递出去。

App.vue

html 复制代码
<template>
  <MyInput ref="myInput"></MyInput>
</template>

<script setup>
import MyInput from './components/MyInput.vue'
import { onMounted, ref } from 'vue'
const myInput = ref(null)
onMounted(() => {
  console.log('~ myInput:', myInput.value)
  myInput.value.focus && myInput.value.focus()
})
</script>

MyInput.vue

html 复制代码
<template>
  <div class="my-input">
    <el-input ref="input" />
  </div>
</template>
<script>
import { ElInput } from 'element-plus'
export default {
  components: {
    ElInput
  },
  mounted() {
    console.log('refs', this.$refs.input)
    for (let key in this.$refs.input) {
      // 将组件 input 上的方法,添加到该组件本身,也就是 MyInput 组件
      this[key] = this.$refs.input[key]
    }
  }
}
</script>

在 App.vue 中调用子组件的 focus 方法:

以此实现,父组件调用子组件的方法。

相关推荐
mldong7 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排7 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60618 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角9 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!9 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰9 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi11 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen12 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马12 小时前
AI源码分析:拆解模型底层实现逻辑
前端
BillKu12 小时前
全局样式变量:CSS 自定义属性(--border-color)和SCSS 变量($border-color)的说明
javascript·css·scss