组件库设计和实现
1. 你在组件库中负责什么?
我是核心成员,主要负责:
- 复杂组件的设计实现(如瀑布流、车牌号组件)
- 组件库工程化建设(文档站、按需加载、CI 流程、测试)
- 代码审查和 issue 处理,把控组件质量
- 优化组件、讨论新组件需求
2. 组件库设计思想
目标:提效、体验一致、可维护
原则:
- API 简单直观,复杂场景可扩展
- 高内聚低耦合,组件间通过 props/事件通信
- 向下兼容,破坏性变更要有 deprecate 方案
- 支持主题定制、全局配置
架构分层:
- 基础层:设计变量、基础样式、动画
- 工具层:工具函数、通用 hooks
- 基础组件:Button、Input、Toast、Dialog 等
- 业务组件:表单封装、表格封装等
- 工程层:文档、按需加载、测试、CI
3. 组件封装与二次封装技巧(重点)
3.1 封装通用组件的考量
- 通用性:提炼核心功能,避免耦合具体业务逻辑
- API 设计:Props 命名规范,提供合理的默认值和类型校验
- 可定制性:通过插槽(Slots)和 CSS 变量支持自定义内容和样式
- 受控与非受控:支持
v-model双向绑定,也支持默认状态 - 兼容性、拓展性,方便升级
- 清晰的文档,提供示例和 API 文档、注释、类型定义
3.2 二次封装第三方组件(如基于 AntD/El 封装业务组件)
Q: 如何透传属性和事件? 使用 v-bind="$attrs" 将父组件传递的非 Props 属性和事件直接透传给内部第三方组件。
Q: 如何透传插槽? 遍历 $slots 动态渲染,确保第三方组件的插槽能正常使用。
vue
<template>
<el-input v-bind="$attrs">
<!-- 插槽透传见下文 -->
</el-input>
</template>
<template>
<el-table v-bind="$attrs">
<template v-for="(val, key) in $slots" #[key]="scope">
<slot :name="key" v-bind="scope" />
</template>
</el-table>
</template>Q: 如何暴露内部组件的方法(Ref)? 使用 defineExpose 暴露内部组件实例,否则父组件无法调用第三方组件的原生方法(如 focus, validate)。
ts
const tableRef = ref();
defineExpose({
// 暴露 tableRef 的方法
...tableRef.value,
});5. 代表性组件:瀑布流
核心方案
异步队列 + 绝对定位。维护列高度数组,找最短列放入新卡片,用 transform: translate3d GPU 加速。
核心难点:图片高度不确定
设计 layoutQueue 队列,串行等待图片加载完成后再排版,配合超时和错误兜底,防止布局错乱。
关键实现
- 布局算法:维护
columns数组记录每列高度,找最小高度列放入,更新列高,容器高度取最大值 - 异步队列:新 Item 进入队列,等待图片 onload 获取真实高度后计算位置,设置 maxWait 超时机制
- 错误处理:5 种策略(默认高度/占位图/重试/完整降级/跳过),确保异常场景可用性
- 性能优化:
shallowReactive减少响应式开销,16ms 防抖,增量更新避免重复计算 - 生命周期:监听页面 onShow/onHide,页面隐藏时中断排版,恢复时继续,避免内存泄漏
成果
- 支持动态增删、自动加载、跨平台适配
- 500+ 项目场景下重排耗时 < 100ms
6. 踩过的坑
跨端兼容:同一样式在小程序和 H5 表现不一致,后来抽象适配层统一管理差异。
API 设计:早期 API 太针对具体场景,后面需求多了只能堆 props。后来在不破坏兼容的前提下,增加更通用的配置方式,通过 deprecate 提示引导迁移。
7. 一句话总结
理解组件库分层架构和设计原则,有从 0 到 1 实现复杂组件(瀑布流)的完整经验,关注性能、稳定性和跨端适配。
