以下是图片内容的提取与整理,采用清晰的 Markdown 排版:
3. 组件设计需要考虑那些?
封装一个公共组件时,主要需要考虑以下几点:
1. 通用性
- 组件的设计需要足够细致,保证在不同场景下都能通用。
- 提炼核心功能,避免组件过于特定于某个业务场景。
2. 清晰的 API 设计
- 定义明确的 props、事件、插槽等接口,让其他团队成员可以快速理解每个参数的用意。
- 考虑默认值、类型校验和必要的错误提示,降低使用难度。
3. 可定制性
- 提供简洁的扩展接口或者通过样式、插槽等方式允许自定义内部行为和样式。
- 保证在不修改组件内部实现的前提下,实现自定义效果。
4. 内部逻辑和状态管理
- 尽量将组件内部的状态和逻辑封装起来,仅向外暴露必要的接口。
- 处理好异步数据、事件绑定等问题,防止组件过于臃肿。
5. 兼容性与扩展性
- 考虑到版本更新和未来扩展,设计时尽量减少对外部组件的依赖或者耦合。
- 通过合理的封装模式,方便后续维护和升级。
6. 性能优化
- 考虑组件在多次渲染时的性能问题,如合理使用防抖、节流、缓存等。
- 根据具体需求选择合适的渲染方式,确保组件在复杂场景下依然高效。
7. 可维护性和文档
- 提供清晰的文档和使用示例,便于团队成员理解组件逻辑和调用方式。
- 定期整理代码和注释,确保组件在多人协作环境下易于维护。
4. 二次封装组件需要考虑那些?
二次封装第三方组件库的组件,需要在原有的组件设计层面增加许多考虑:
1. 如何将所有的属性和事件传递给第三方组件?
- 此处可以使用
$attrs将属性穿透给子组件,它包含了所有 props 中未声明的属性和事件。 - 如果需要类型提示,则可以看一下第三方组件库有没有提供类型声明的方式。
2. 如何将插槽传递给第三方组件?
基本上有两种方式,一种是通过循环的方式遍历所有插槽传递给子组件;另一种是使用 h 函数渲染插槽:
vue
<template>
<component :is="h(AInput, $attrs, $slots)"></component>
</template>代码逻辑解释:
- 通过
component动态组件渲染,这里使用h函数创建一个 vnode。 h函数第一个参数是AInput,表示要渲染AInput组件。h函数第二个参数是$attrs,表示将所有的属性穿透给AInput。h函数第三个参数是插槽($slots),表示将当前组件接收到的所有插槽传递给子组件。
这样就很简单地完成了插槽穿透,同时也会将属性一并传递过去。
3. 如何将子组件暴露的方法全部暴露出去?
比较传统的方案是绑定一个 ref 给子组件,然后在组件挂载完成后遍历子组件的 ref,将子组件暴露的方法传递出去。这种方案不太稳定,因为当子组件存在 v-if 时,后续可能会被销毁,导致 ref 依然保留引用,出现内存泄漏。
更好的方式是利用 defineExpose 的原理:defineExpose 会把接收到的对象放到 instance.exposed 上面。
javascript
const instance = getCurrentInstance()
function fn() {
console.log('暴露fn方法给父组件')
}
defineExpose({
fn
})
// 等同于以下代码
instance.exposed = { fn }因此,我们可以给 Component 的 ref 传递一个函数。当组件挂载时,子组件的实例会通过这个函数传递过来:
vue
<template>
<div>
<div>自己写的内容</div>
<component :is="h(ElInput, { ...$attrs, ref: changeRef }, $slots)"></component>
</div>
</template>
<script setup>
import { ElInput } from 'element-plus'
import { getCurrentInstance, h } from 'vue'
const instance = getCurrentInstance()
function changeRef(exposed) {
// 这样就相当于 defineExpose(exposed)
instance.exposed = exposed
}
</script>通过这种方式,可以避免子组件销毁时所带来的副作用问题。
