前端组件化:Vue/React组件设计思想与遵从原则
Author:zhoulujun Date:
组件和插件的区别是什么呢?插件是集成到某个平台上的,比如 Jenkins 插件、Chrome 插件等等,jQuery 插件也类似。平台只提供基础能力,插件则提供一些定制化的能力。
而组件则是偏向于 ui 层面的,将 ui 和业务逻辑封装起来,供其他人使用。
在一些最简单无脑的 jQuery 插件中,它们一般会将 DOM 结构直接写死到插件中,这样的插件拿来即用,但限制也比较大,我们无法修改插件的 DOM 结构。
// 轮播图插件 $("#slider").slider({ config: { showDot: true, // 是否展示小圆点 showArrow: true // 是否展示左右小箭头 }, // 一些配置 data: [] // 数据 })
还有另一种极端的插件,它们完全不把 DOM 放到插件中,但会要求使用者按照某种固定格式的结构来组织代码。
一旦结构不准确,就可能会造成插件内部获取 DOM 出错。但这种插件的好处在于可以由使用者自定义具体的 DOM 结构和样式。
<div id="slider"> <ul class="list"> <li data-index="0"><img src="" /></li> <li data-index="1"><img src="" /></li> <li data-index="2"><img src="" /></li> </ul> <a href="javascript:;" class="left-arrow"><</a> <a href="javascript:;" class="left-arrow">></a> <div class="dot"> <span data-index="0"></span> <span data-index="1"></span> <span data-index="2"></span> </div> </div> <script> $("#slider").slider({ config: {} // 配置 }) </script>
当然,你也可以选择将 DOM 通过配置传给插件,插件内部去做这些渲染的工作,这样的插件比较灵活。有没有发现?这和 render props 模式非常相似。
$("#slider").slider({ config: {}, // 配置 components: { dot: (item, index) => `<span data-index=${index}></span>`, item: (item, index) => `<li data-index=${index}><img src=${item.src} /></li>` } })
前面讲了几种 jQuery 插件的设计模式,其实万变不离其宗,不管是 jQuery 还是 React、Vue、AngularJS,组件设计思想都是一样的。
为什么要组件化
组件化是对实现的分层,是更有效地代码组合方式
组件化是对资源的重组和优化,从而使项目资源管理更合理
组件化有利于单元测试
组件化对重构较友好
组件设计原则
适当的组件粒度:一个组件尽量只做一件事。
复用相同部分:尽量复用不同组件相同的部分。
松耦合:组件不应当依赖另一个组件。
数据解耦:组件不应该依赖特定结构的数据。
结构自由:组件不应该封闭固定的结构。
容器组件与展示组件
顾名思义,容器组件就是类似于“容器”的组件,它可以拥有状态,会做一些网络请求之类的副作用处理,一般是一个业务模块的入口,比如某个路由指向的组件。我们最常见的就是 Redux 中被 connect 包裹的组件。
容器组件有这么几个特点:
容器组件常常是和业务相关的。
统一的数据管理,可以作为数据源给子组件提供数据。
统一的通信管理,实现子组件之间的通信。
展示组件就比较简单的多,在 React 中组件的设计理念是 view = f(data),展示组件只接收外部传来的 props,一般内部没有状态,只有一个渲染的作用。
Vue组件库包含组件(Component)、指令(Directive)和过滤器(Filter)三种类型的存在。
组件多层嵌套时,应该把Modal放在根组件里,然后在子组件里通过事件触发。在具体应用里,应该这么用,这符合Vue提倡的“状态驱动”。
组件化的工作方式信奉独立、完整、自由组合。目标就是尽可能把设计与开发中的元素独立化,使它具备完整的局部功能,通过自由组合来构成整个产品。
从页面元素的可复用性角度考虑,我们将将组件按类型分为公众组件、容器组件和视图组件。
模块(Module)通常强调的是职责(分离、内聚),组件是可复用模块和相关依赖的封装。
组件可以如下定义:
可复用的模块,完成既定功能
有明确的接口规定
有上下文依赖、外部依赖资源的定义
可以独立发布
组件设计的一些理念
在设计组件的时候需要考虑到很多方面,以便它们可以很好的复用,组合,分离和低耦合
以下原则尽可能使用
单一职责原则
组件里的每个模块,分别该承担某一个功能
多个组件 / 模块协同完成一件事,而不是一个组件替其他组件完成本该它自己完成的事情
开放封闭原则
属性配置等 API 对外开放;组件内部 dom 及状态的更改、对外封闭
高内聚、低耦合
组件内部通过 callback 方式直接调用,组件与组件之间通过发布订阅的模式通信
API 尽量和已知概念保持一致
API 命名:比如 聚焦 常用命名是 focusable 而不是 canFocus 等自己臆想的名字、还有如 onDeselect 等规范名字。
API 的功能要单一并表意:比如 active 表示活动状态、但不能代替表示 selected 选中状态。
追求短小精悍
避免太多参数
缩小信赖范围和向稳定方向信赖
适用SPOT法则 (Single Point Of Truth,就是尽量不要重复代码,出自《The Art of Unix Programming》)
无副作用
引用透明
避免暴露组件内部实现
避免直接操作DOM
适用好莱坞法则 (好莱坞法则: Don’t call us, we’ll call you, 又称IoC, Inversion of control, 控制反转)
入口处检查参数的有效性,出口处检查返回的正确性
充分隔离变化的部分
组件和数据分享,信赖一致性的数据结构
以下 8项是我认为值得去注意的
层次结构和 UML 类图
扁平化、面向数据的 state/props
更加纯粹的 State 变化
低耦合
辅助代码分离
提炼精华
及时模块化
集中/统一的状态管理
单一职责
组件拆分目的:复用与隔离
隔离的类型,组件业务必然很重,此时虽然要保证组件尽可能简单,
复用类型的,通用性更强,所以功能越单一,使用起来就越方便。
我们知道 react 有一个概念:container/component,
即 component 只是渲染组件,而 container 才是产生业务的组件,我们 Vue 也可以依照这个理念进行设计。
即把数据处理等带有副作用的工作放在父组件中,而子组件只进行展示或操作,通过事件的方式让父组件进行处理,
保证逻辑归一,后续维护也更为方便。或者使用 slot 等类似高阶组件的方式来简化当前组件的内容。
无副作用/引用透明
和纯函数类似,设计的一个组件不应该对父组件产生副作用,从而达到引用透明(引用多次不影响结果)。
数据操作前必须进行复制。比如需要添加额外的键值,或者需要对数组类型的数据进行操作,会对原始数据产生影响
注:引用类型的 props 千万不要直接修改对象,虽然能够达到传递数据的目的,但会产生副作用,如果有其他地方用到该数据,可能产生未知的影响。
组件划分颗粒度
组件拆分出来之后,拆成几层或者是拆成几块,影响文件的数量。如果层级比较多,各种 props 传递,事件传递,维护成本比较高。
保证逻辑处理集中在一个组件,维护也比较方便。
新功能下添加新属性/新文件
对于通用类型组件,我们要求它尽可能的短小精悍,调用起来更为简单,所以不能设计太多的参数。基础组件库不能符合这个要求,
主要是因为基础组件库需要尽可能增加普适性,不会因为没有某个常用的属性,导致该组件需要复制一份重写,再加上日积月累的 pull request,
属性和参数必然会越来越多。而我们在业务中使用,完全不需要这么多的配置,如果有重大差别,重新复制一份,对于后续的维护反而更方便。
所以是否新增加属性还是拷贝一份,是根据后续该组件是否会产生比较大的发展方向差异来决定的。
Vue 组件之间的交互设计
Vue 组件与 React 组件有比较大的区别,模板的设计更偏向于 HTML,所以要实现类似 react 的高阶组件的需求通常比较少,
而高阶组件集成度过高,对于业务来说,当业务越来越复杂,组件内部逻辑将拆分困难,未必是件好事,所以我们只讨论普通的组件设计。
组件设计是考虑组件通讯方式,主要分为以下几个方面:数据流转(向下传值,向上传值),伪双向绑定,方法调用。
简化与抽离的其他实现
使用插件或者 mixins 实现
抄袭源头:
浅谈 React 组件设计 https://github.com/yinguangyao/blog/issues/40
如何去设计一个组件封装_前端组件化设计思路 www.fly63.com/article/detial/996
漫谈Vue组件库开发 https://jdc.jd.com/archives/212167
[译] 前端组件设计原则 https://juejin.im/post/5c49cff56fb9a049bd42a90f
前端工程——基础篇 https://github.com/fouber/blog/issues/10
前端组件设计杂谈 warmhug.github.io/2018/09/09/components-design-experience.html
化整为零!关于组件化设计升级的一些思考 https://www.imspm.com/article/1499653624086?p=1&m=0
转载本站文章《前端组件化:Vue/React组件设计思想与遵从原则》,
请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/vue/8526.html