当前位置: 首页 > article >正文

UI 组件的二次封装

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

1. 二次封装的原理

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

核心思想包括:

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

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

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

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

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

2. 原理解释

1、对属性和事件的传递

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

在 App.vue 中:

<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 中:

<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 获取父组件传递的数据,展示为:

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

<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 中,可以将这些属性透传给 原始组件 本身。

如下:

<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 中,传递两个插槽内容。

<template>
  <MyInput>
    <template #prepend>
      <el-button>prepend</el-button>
    </template>
    <template #append>
      <el-button>append</el-button>
    </template>
  </MyInput>
</template>

在 MyInput 组件中:

<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

<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

<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 方法:

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


http://www.kler.cn/a/371823.html

相关文章:

  • 计算结构力学:多自由度振动系统
  • WPF+MVVM案例实战(十一)- 环形进度条实现
  • 基于SSM+小程序的智慧旅游平台登录管理系统(旅游2)
  • (二十三)、k8s(minikube) 部署mysql
  • 鸿蒙学习总结
  • MySQL进阶版
  • 获取平台Redis各项性能指标
  • socket编程---UDP
  • Python应用指南:利用高德地图API实现路径规划
  • 代码随想录训练营Day11 | 226.翻转二叉树 - 101. 对称二叉树 - 104.二叉树的最大深度 - 111.二叉树的最小深度
  • 高级java每日一道面试题-2024年10月24日-JVM篇-说一下JVM有哪些垃圾回收器?
  • Javascript进阶
  • golang包导入注意事项
  • 基于SSM+小程序的垃圾分类管理系统(垃圾3)
  • Notion + Python + scholarly = 超强文献管理助手
  • 神经网络的常用layer
  • vue使用prototype
  • 【Java Maven框架】
  • 五个我经常使用的前端开发的库
  • 【机器学习】任务九:卷积神经网络(基于 Cifar-10 数据集的彩色图像识别分类、基于 CNN 的手写数字识别的实验)
  • 基于java的山区环境监督管理系统(源码+定制+开发)环境数据可视化、环境数据监测、 环境保护管理 、污染防治监测系统 大数据分析
  • 【C++】string 类深度解析:探秘字符串操作的核心
  • python如何完成金融领域的数据分析,思路以及常见的做法是什么?
  • 【Django】创建项目、启动及app过程及遇到的问题和解决方案
  • Firefox和Chrome谁的插件生态系统更完善
  • 8年经验之谈 —— 如何使用自动化工具编写测试用例?