tags: [components]
categories: 业务场景解决方案
前言
已有组件 OR 自造组件???
基础和灵活 VS 定制化和效率
- 垂直行业领域专业性组件
- 强主题风格组件
- 强交互组件
- 甚至是同款组件,不同配置
组件 -> 通用组件 -> 通用组件库
|-> 业务组件 -> 业务组件库 -> 行业柱(n) -> 组件平台 -> 物料市场
自造组件我们得到了什么
[x] 规范 =》 体系
产品功能迭代
技术变更(UI 设计层可以不变)
人事变动、人才流失
沟通(跨部门的共通语言)
客户需求多样性
[x] 效能
- 团队成长
- 走出去的可能
什么样的产品需要组件赋能
- 量极大功能复杂
- 产品项目变动频繁,并发很多
- 产品间往往有较高的关联性
- 产品间存在共性
- 使用者具备很强的专业性
- 开放的趋势
设计体系
组件纬度
- Atoms原子。 为网页构成的基本元素。例如按钮,也可以为抽象的概念,例如字体、色调等。
- Molecules分子。 由原子构成的简单UI组件。例如,表单标签,搜索框和按钮共同打造了一个搜索表单分子。
- Organisms组织。 由原子及分子组成的相对复杂的UI构成物。
- Templates模版。 将以上元素进行排版,显示设计的底层内容结构。
- Pages页面。 将实际内容(图片、文章等)套件在特定模板,页面是模板的具体实例
统一的视觉风格和交互规范
X
组件在 A
业务场景是一个交互,在 B
业务场景是另一个 UI 风格,这样就无法对 X
进行抽象,极大的增加了组件的构建成本。所以,设计组件之初,首先需要抽象和约定一套统一的视觉风格和交互规范。
足够的可扩展性
组件在落地到实际项目中时,有各种不同的需求,默认的定制化并不能适用于任何的场景,只有具备良好拓展性的组件才能应用到不同的业务线,以“不变应万变”
一定的可控性
组件封装有一定的不确定性,更多时候是在做几个方面的权衡,并且在业务不断变化中,可能还会面临一些调整和重构。将不确定性控制在一定范围内。
组件适用性检验
源于业务,归于业务。确保产出可以快速的在实际场景中得到验证,快速试错。
- 通过开源的方式获取更多的反馈
- 通过内部项目落地迭代组件API
产出物
- 产品和技术结合最为紧密的部分
- 最为稳定及复用程度最高的部分
- 一份文档多人使用
- 开发者说明
- 设计体系规范
- 代码片段、工具函数
- 培训文档
- 社区化:灵活的沟通机制
前端体系
代码规范
代码 review
工具检验
集成了 eslint、stylelint、git hook 的前端代码检查工具。
- 初始化 eslint/stylelint 配置文件
- 安装 eslint/stylelint 及其依赖到当前项目的 node_modules 里
- 挂载 git 钩子,在提交代码时进行强制校验
拥抱 prettier
eslint-config-prettier: 关闭和 eslint 冲突的所有的规则
vscode 插件
组件设计思路
在UI层面视觉上的组件是一种产品形式,界限是“视觉可分”,但是在代码层面,也会存在“逻辑可分”,所以开发者开开发前应对业务场景进行分析,把控代码层的组件粒度。
什么情况下需要抽离组件
不能狭隘地认为只是为了复用(包括对于模块化的理解),认为只有一个地方用就没必要抽取封装为组件
- 组件化是对实现的分层,是更有效地代码组合方式
- 组件化是对资源的重组和优化,从而使项目资源管理更合理
- 组件化有利于单元测试
- 组件化对重构较友好
组件封装原则
高复用性 + 少组件 + 大组件
- 高复用性:对项目中所有Web页面的结构有清晰的把握,能准确提取样式或功能雷同的模块并将其封装成组件
- 少组件:当深层次的成员无需组件化时,可直接将外层的父元素组件化以减少组件的数量,该原则是基于开发效率考虑的
- 大组件:应更多的将零散组件拼装成更大的组件,可以简化组件的调用,提升团队开发效率
容器组件 + 展示组件
- 容器组件:处理数据、逻辑(数据格式化);对接后端API,格式改变则改变该组件
- 展示组件:处理样式;对接视觉设计及用户体验,这些组件不关注自己何时何处被渲染
Functional Components
应该最大限度的编写和使用 Functional Components : 无状态、无方法、纯组件
数据模型
原则:对数据切分时,props的优先级要高于state,尽可能保证组件的无状态化,简化组件的交互逻辑
- 共享数据(固定数据):props对象(单向流动性|显示传递性|只读性) && store
- 私有数据(动态数据):state对象
需要反复琢磨的问题
Q1:这个组件可否(有必要)再分?
组件划分的依据通常是 业务逻辑 功能,要考虑各组件之间的关系是否明确(如组件树方式管理组件间依赖关系,兄弟组件不可见),以及组件的可复用度。
划分粒度的大小需要根据实际情况权衡,太小会提升维护成本,太大又不够灵活和高复用性。
每一个组件都应该有其独特的划分目的的,有的是为了复用实现,有的是为了封装复杂度清晰业务实现。
Q2:这个组件的依赖是否可再缩减?
缩减组件依赖可以提高组件的可复用度,常用的方法是IoC(依赖注入),对外弱类型依赖。
Q3:这个组件是否对其它组件造成侵入?
一个组件的封装性不够,或者自身越界操作,就可能对自身之外造成了侵入,这种情况应该尽量避免,确保组件的生命周期能够对其影响进行有效的管理(如destroy后不留痕迹)。
较常见的一种情况是:组件运行时对window对象添加resize监听事件以实现组件响应视窗尺寸变化事件,这种需求的更好替代方案是:组件提供刷新方法,由父组件实现调用(最终由根组件统一处理)。次优的方案是,当组件destroy前清理恢复。
一个组件不应对其它兄弟组件造成直接影响。
Q4:这个组件可否复用于其它类似场景中?
需要考虑需要适用的不同场景,在组件接口设计时进行必要的兼容
Q5:这个组件当别人用时,会怎么想?
接口设计符合规范和大众习惯,尽量让别人用起来简单易上手,易上手是指更符合直觉。
假如业务需要不需要这个功能,是否方便清除?各组件之间以组合的关系互相配合,也是对功能需求的模块化抽象,当需求变化时可以将实现以模块粒度进行调整。
开发流程
组件初始化: 自动创建组件需要的目录和模版代码
组件 Coding
组件 Demo
组件文档
组件测试
[x] 开发者自测 UI 及 API
- 开发者互测API
- UI 人肉测试
- 开发者单元测试
测试流程
开发者自测 UI 及 API
开发者根据UI设计稿开发完成后,自己根据UI要求及文档分场景根据文档生产工具的 playground 完成自测。
⚠️极端数据测试
- 数据为空时
- 数据接口调用错误时
[ ] 请求参数极端情况
数据特别小时
数据特别大时
为null,NaN,undefine
开发者互测 API
开发者团队内部通过结对编程的方式完成组件的API互测。
开发者单元测试
React 生态圈会推荐使用 jest + enzyme 配套工具
Jest是 Facebook 开发的一个测试框架,它集成了测试执行器、断言库、spy、mock、snapshot和测试覆盖率报告等功能。React 项目本身也是使用 Jest 进行单测的,因此它们俩的契合度相当高。但是使用 jest 做 UI 测试有局限性,只能测试基本的 dom 结构 和 样式,一些逻辑交互无法测到,只能覆盖大部分的情况。
Enzyme是由 airbnb 开发的 React 单测工具。它扩展了 React 的 TestUtils 并通过支持类似 jQuery 的 find 语法可以很方便的对 render出来的结果做各种断言。
UI 人肉测试
开发团队测试流程全部走完后,提交到UI设计者测试流程,完成组件UI层面场景、视觉、交互的评审。
发包流程
组件发包只有拥有发包权限的人才能操作,Dbox 是以组件库为单位发包的,使用命令 yarn publish
发包,在发包之前会跑组件测试,只有测试通过以后才能发包。
维护流程
组件日常维护占整个组件库生命周期的很大一部分,组件库做起来了以后,组件功能后续会不断迭代,也许是 bug fix,也可能是 new feature。
- PR / issue 的处理:PR规范
- 发包
- 管理 changelog
按需加载工具
借助babel-plugin-import实现
Babel 插件手册
通过不同环境切换不同的入口
'use strict';
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react.production.min.js');
} else {
module.exports = require('./cjs/react.development.js');
}
项目落地
- UI组件
- 脚手架
- 使用文档
- 项目官网及社区
- 命令行
- 接口数据 mock 工具
- 构建工具栈
- 业务组件: 在业务组件库中,不必那么灵活,支持业务需要的属性配置即可。可以封装业务相关的代码进去,而不用担心影响其他业务线,更高的封装程度和业务属性会让该业务线的开发体验和效率提升一个量级
- 数据上报
- 监控体系(错误监控、性能监控、业务监控、服务监控)
- 持续集成: 部署流程和部署平台
- 自动化测试
后记
组件化这条路上,我们还有很多事情要做
参考资源
React 组件设计模式基础 https://juejin.im/post/5a73d6435188257a6a789d0d
谈谈 Vue 业务组件 https://juejin.im/entry/5a97b8bc518825555b2e1845