组件封装原则
常见目录设计
- components 内部目录结构
dialog
对话框loading
加载searchContent
搜索表单table
表格
设计前思考
- 样式
- 组件内该写哪些样式、注意什么?
- 子组件可以先预制一些类,之后传类名即可。
- 子组件的样式权重设置低一点,方便引用的之后覆盖,避免使用
important
- 组件内该写哪些样式、注意什么?
- template
- 放
slot
里还是组件里?- [[固定内容]]可以写死,不确定的通过
slot
传递,也要考虑数据传递 - 可以预先设定内容,判断有没有传
slot
,优先使用slot
的值
- [[固定内容]]可以写死,不确定的通过
- 放
- 行为
- 在父组件定义、还是子组件定义?
- 基本行为 → 子组件操作(关闭、重置按钮等)
- 业务行为 → 让父组件监听,再执行相应的操作
- 还能根据
状态前中后
细分,使这个组件扩展性
更强- 例如:搜索前、中、后;关闭弹框前、后 [[组件行为扩展示例代码]]
- 在父组件定义、还是子组件定义?
- props
- 哪些值通过
props
传递,哪些由子组件自己声明?- 子组件内部定义、行为越少,扩展性就越好,通过
props
传值扩展性会更强
- 子组件内部定义、行为越少,扩展性就越好,通过
- 哪些值通过
- 扩展性 && 便捷性 权衡
- 组件通过
父传值
,扩展性
就会越强 - 子组件
内部定义
的东西越多,便捷性
就越强 - 所以引用的地方越多,组件内部行为就应该少一些,多让父组件去执行相应的操作
- 组件通过
封装业务组件的 2大原则
不结合具体业务逻辑
- 数据:一般组件只是最为一个容器,由父组件传入。
- 操作:子组件负责一些 UI 操作,然后触发父组件的监听,交由父组件进行功能操作。 尽量提供简便
- 让通用性高的子组件易于使用,如有少量差距,做针对性扩展。
组件数据 2种设计方式
- 配置项 (props)
- [[插槽]]
配置项
- 场景:频繁使用的用配置项
- 优点:使用者更方便
- 缺点:
- 不适用于复杂情况
- 健壮性差
- 避免:少传、多传、传错等情况
- 解决:必须要规范使用者传值,并给出引导、提示(这就是
TS
的必要性)
插槽
- 场景:自定义插槽用于扩展复杂的、不常用的组件
- 优点:完全开放,适用于任意情况。
- 缺点:基础的配置需要使用者写一遍
数据流 原则-建议
- 优先使用
computed
,警惕watch
/watchEffect
等 API 的使用。转换思维先从克制使用 watch 开始。 - 适当使用
readonly
, 禁止状态被坏人修改 - 最小化状态。避免创建‘缓存’状态,让数据自然流动,不要阻断。
- 自顶而下,将细节/副作用分流到 hooks 或子组件中,起一个好一点的名字, 让流程看起来更清晰
- 将 watch 转换为 computed 的语义。外观上的差别是 watch 有 callback, 而 computed 是「管道」,会衍生新的数据。比如上面
useRequest
的例子 - 推荐使用 VueUse
- 封装 hooks, 让各种外部的状态或副作用优雅地集成进来
- 单向数据流,对这个有两层理解
- 表示是一种数据流动的方向,通常和 CQRS 模式配合,比如 Redux、Vuex,只能单向的修改和查询
- 表示一种数据管辖的范围。 通常应用只有数据的拥有者才有权限变更。进一步地讲,我们应该以组件为边界,来限定数据的管辖范围。需要变更时,通过‘事件’ 来通知拥有者。比如 严格遵循 v-model 协议。
- 使用响应式开发思维,构造单向的数据流
- 尽量管道化的方式去设计你的程序
- 声明式,不要命令式
- 拆分组件或 hooks 来分治数据流
- 组件之间 props 传递也属于数据流。
- 使用 ref/reactive → computed → watch → handler → render 这样的顺序组织代码
组件封装-常用属性
$attrs
简化多层组件之间 props 传值;$listeners
简化多层组件之间事件传递;$Slots
更多拓展自定义组件传值,包括自定义 html 元素,及对象;props validator
增强组件传值稳健性,可自定义业务代码效验参数;$refs
对外提供 API 增强组件灵活度和可控性;
form 表单组件封装
- 普通表单
- 通过组件组合生成的表单
- 动态表单 [[动态表单]]
- 通过配置代码生成的表单
生成动态表单的配置代码,一般来说会存储在后端,并通过 API 提供给前端使用。
组件设计参考
UI设计交互
就好比模块的 import、exports,要保持统一的出口。即统一风格配色,统一交互。基本功能
就好比模块的基本作用。必须保证基本功能的同时,兼容其他。业务拓展
就好比模块间的集成,这就要求组件可依耐性低、可拔插集成、高聚低耦合。性能
要考虑到大量数据或大量计算与 GUI 渲染线程互斥导致的性能问题。
疑问
父组件中请求数据好?还是子组件中请求数据好?
1、如果现在有一个父组件,3 个子组件。 每个组件展示的数据不同,有 2 种数据获取方式。 第一种:在父组件中获取所有的数据,然后使用 props 传递给每个子组件。 第二种:各个子组件中获取各自的数据。
[!question] 你觉得哪种好?为什么? 答:根据实际业务来
如果数据其他组件用不到, 那么可以由子组件自己获取
如果数据可能被同级组件 复用 共享
那么由父组件统一获取然后分发下去
如果数据可能被跨级组件 复用 共享
那么你需要vuex
或者eventbus
统一分发至不同级别的组件子组件复用率高
或耦合性小
则独立获取数据, 子组件如果跟父组件关系紧密则父组件获取数据
心得
要考虑心智成本和开发成本
不要大而全,让使用者自己组合;也不要过度封装增加使用成本和维护成本,找好平衡。
准备两个方案,考虑便利性和扩展性
少用 v-if
,尝试使用动态组件切换
做好传值校验(结合TS)