react hooks UI与业务逻辑分离必要性技术方案
trigkit4 人气:3引言
当前端业务复杂度上升到一定程度的时候,如何提升前端代码质量便成了老生常谈的话题。似乎前端总逃不开改他人代码,重构,修复bug的宿命。那么,我们要如何从项目代码层面,改变这一局面呢?才能保证项目A之于开发者B也是能有条不紊的介入开发,从而最大程度降低人员开销,实现真正降本提效呢?
从代码层面的问题上看,我列举了下,大概有如下几种:
- 工程化没有做好各类lint检查和约束
超长的function
- 很难单从函数名看出这个函数是作什么的
- 一个函数做了十件事
代码量超长的模块
- 内部维护了非常多逻辑,很难一眼看清某个变量是在哪里被修改的
- 一个组件夹杂了这个组件所需的所有代码,不懂职责划分的重要性
缺乏清晰的职责划分
- 哪个模块做什么,对于数据应该如何流向,如何改变没有清晰的认知
数据流紊乱
- 缺乏函数式写法的意识
变量命名极不规范
- 变量命名很含糊,能通过命名讲清楚这个函数是做什么的,却很随意对待
检验好代码的唯一标准应该是:人们能否轻而易举地修改它
业务的问题
我们知道,业务迭代往往排山倒海压来,一开始如果不做好全局规划,或者理清各个模块的关系,是很难把控好进度,进而出现赶工导致bug滋生。那么,目前的hooks 业务组件的写法有何问题呢?
基于hooks的纯业务组件写法没有做约束,ui与业务逻辑在一个函数内部维护,面条式代码滋生,容易使组件业务逻辑代码越写越长,久而久之难以维护。很容易出现一个函数内部耦合了types,constants,各类hooks(useState,useReducer,useCallback等),以及各种function,甚至是在dom层夹杂着非常多的逻辑处理。
慢慢地,复用性也会越来越差,可能需要经常重构,抽离代码 以达到复用的程度。但往往业务的排期已经没法抽开身去维护老代码,那怎么办呢?
hooks组件的分离
《重构2:改善既有代码的设计》一文提到:把复杂的代码块分解为更小的单元,与好的命名一样都很重要。
因此,我们需要在团队内部达成共识,能够产出一种固定的开发范式,能够分离代码,做到职责清晰,例如:A模块专门处理View视图组件,B模块专门处理业务逻辑,C模块专门维护ts类型types,D模块专门维护各类常量constants,E模块专门维护公用hooks逻辑,F模块专门维护css modules等。
那么,在这前提之下,我们需要实现前端UI与业务逻辑分离,目前主流的有两种方式,一种是纯逻辑抽离出去,返回函数内部方法和state;形如:
const useApp = () => { const [name, setName] = useState('mike'); const getName = () => {}; const updateName = () => {}; return { name, getName, updateName } } const AppView =() => { const { name } = useApp(); return <div>{name}</div> }
这种方式没什么太大问题,但这种代码不内聚,没法提供通用的逻辑处理,一旦业务发生变化,就会引发多处代码的维护危机。
其次如果有很多业务团队,那么就需要考虑如何规范化统一团队内部写法,如何支持更健壮的业务代码。
UI与逻辑分离并不是最终的目的,最终的目的应该是形成一套易于维护,模块职责划分清晰,能够形成固定开发模式,易于扩展,能够规范化业务使用场景,且具备强壮生命力的方案。
如果这种方式可以实现的话,那么为何很少有人会这么干呢?原因可能在于大家的函数式组件的思维。
在hooks还没诞生之前,大家普遍对于函数式组件的认知就是没有state,所以当props是固定的,那么函数式组件每次渲染结果也都是一样的,也就是相同的输入总能得到相同的输出。但现在hooks出现了,函数组件内部可以维护state了,相同的输入并不一定能得到相同的输出了。
此外,这种方式与可复用的hooks的区别又在哪里,如果两种都使用hooks维护,又如何区分呢?
另外一种方式就是保留业务逻辑,但把UI组件抽离出去,这种方式更不推荐了。有点类似子组件,父子组件通信的既视感随之袭来。
接下来,我们再来看下纯hooks组件饱受大家诟病的一些问题:
纯hooks组件的问题
1、useState 写法难用,如果有很多state,需要一个个去维护,写法不够简洁;当业务逻辑越来越复杂,往往会出现一个模块几十个useState需要维护的尴尬局面。
2、useReducer + context
的全局状态难用,仍然需要定义很多action type
,还需要提供provider,使用useReducer跨组件共享状态很麻烦
3、useCallback 用法不够清晰,不知何时用何时不用,用法造成困惑
4、 生命周期需要引入useEffect,需要手动管理,且不够语义化
5、基于hooks的业务组件,内部方法依然难以做到复用,应抽离出去单独维护。
6、当使用useEffect模拟mounted事件时,处理异步请求函数时很麻烦。
7、当组件达到一定复杂度的时候,堆积到一起的代码会变得越来越难以维护
8、React Hook的闭包陷阱问题
9、useState 调用updater更新后,无法同步获取最新state值
10、useState updater无法实现细粒度更新对象的属性值,不得不浅拷贝一份数据再进行覆盖
hooks-view-model
想要写出健壮的,长期可持续维护的代码,就必须去理解这些在其他编程领域通用的设计模式、原则、范式。提高代码质量,除了依赖开发自测和相关流程规范化外,也应有相关工具或统一的开发范式做约束。
对于纯写业务的人来说,没有规范去强制约定,那么几乎没有人会这么处理业务逻辑与UI的关系,最终还是会写到一起。这是hooks这种弱约束的弊端。
基于上述问题,我开发了基于react hooks的UI与业务逻辑分离的方案,内部基于useState hooks的updater 实现。可实现在class内部setState,然后在View组件中响应更新。基本解决了上述react hooks的十个“老大难”问题
hooks-view-model
是一种通过拆分UI视图与业务逻辑的解决方案,可做到无需useReducer,无需redux等技术方案实现全局状态更新而不会渲染无关组件。hooks-view-model
是集状态管理,变量的存储管理和数据的持久化管理于一体的解决方案。
详情点击
加载全部内容