2020 转眼已来到3月,但疫情的突袭,让这个春节迟迟没有开始,也无法结束。这段时间看似是充电自我提高的大好时光,但家国情怀深厚的我为疫情真的是操碎了心,时不时都要看看哪里数据猛增了,哪里暴发了。结束一个月的在家办公,带着口罩在公司上班,状态稍有好转,注意力终归回到了技术。 前端
最近在组里推BFF Node接入与微前端改造,看到了组里各式各样实现地业务代码(组件),有激进开放的整个页面都用hooks实现,有沉迷于过去的停留于redux + saga + model的类组件写法,固然更多的是类组件中的子组件参和几个hooks。我带着好奇之心去goggle了一下这两个谁胜一筹,因而有了此文。react
网上看了不少大佬观点,但脱离业务的争论都是耍流氓。做为眼见为实躺坑无数的练习生,我决定用事实说话。说那么多干吗,上代码:
慢...,先说说接下来要干的事情:git
发现没,这就是一个标准的CRUD
,页面大体长这样:github
接下来,列一下两种方式实现,页面的大体结构,橙色方框内为Hook重构后的页面结构:
Hook重构后代码变化主要在index.js,删去了对Dva的Model依赖,转由Hook本身管理数据, 代码对比大体以下:
看代码,主要是数据层的实现有变化,UI保持一致。当页面跑起来,若是不看面包屑的话,也很难分辨谁是Hooks组件的实现,谁是类组件的实现,因此表面的看,是看不出谁高谁低的。spring
毕竟人的肉眼,只有当低于30fps时,才能明显的观察到变化;因此要想客观的对比性能,得依赖Chrome的性能分析(Performerce);这里作两个对比:chrome
跳转到列表页 redux
打开编辑页 缓存
为保证试验结果严谨,我尽可能保证惟一性变量原则、多(san)次、与减小本身手抖的次数,但与自动化测试仍是有较大差距,十几毫秒的偏差再所不免。我观察了列表页切换页
的两个图,除开请求的波动于手动测试的偏差,两个页面从点击到请求再到页面渲染,时间相差无几,甚至调用栈都是惊人的类似(装逼的说法就是:从原理上分析,也应该是类似的:dispatch + diff + render)。
也观察了详情编辑Modal打开
两个图,屡次测试,其打开速度hooks稍暂上风,因为这一块的实现逻辑有比较大的差异(class
组件的数据获取是在最外层,获取完而后依次向里传递;而hooks
则是从外层获取到id后,组件内部直接发起请求获取数据),因此二者火焰图也有比较大的差别;但从页面渲染的角度总体感受差异不大。 性能优化
最后我得出的见解是:Hooks更可能是一种管理数据的手段,与class相比,并无什么性能上的优点,更多的主动权,在编写代码的人手里,就像我驾校老师爱说的那句狗屁不通的谚语:再好的车,给这个二傻子开,都能开熄火。关于更多,能够关注B乎讨论: React hooks 和 Class Component 的性能哪个更好?。
若是对个人测试有疑惑,能够本身动手,我提供示例项目:antd
我我的始终同意:框架只是实现业务的手段,在使用成熟的框架前提下,页面的性能彻底由司机掌控。(我我的有个观点就是:Vue是自动档,React更像手动档
)
要想彻底脱离redux或mobx,简单使用Hooks中的useState或useEffect来完成页面,其难度仍是很大且很难管理
,毕竟页面大多数数据源都来自异步请求,因此封装一个useRequest hooks是势在必要的,并且Hooks最大的优点就是逻辑复用。如下将分享部门封装useRequest组件的思考过程,其思路参考于Apollo-Graphql
的react-hooks项目。先看示例代码(上面列表页的部分代码):
export default function Root() { const [search, onSearch, onReset] = usePagination({}); // 请求:admin.closertb.site/rule/query接口 const { data = {}, loading, error } = useRequest('/rule/query', search); const { datas, total } = data; const tableProps = { search, datas, fields, onSearch, total, loading, }; const searchProps = { fields: searchFields, search, onReset, onSearch, }; return ( <div> <WithSearch {...searchProps} /> <div className="pageContent"> <EnhanceTable {...tableProps} /> </div> </div> ); }
以上就是一个最多见的useRequest hook应用,用很是简短的代码替换了Dva中的路由监听(subscription
), 异步请求(effect
),数据更新(reducer
)等一连串逻辑;下图是一个简单的流程示意图:
根据这个示意图,老司机应该就大概知道怎么实现的了:
useRef
来缓存请求实例,即每一个useRequest仅建立一个query实例,页面更新时,沿用已经存在的示例;useMemo
作计算,判断请求参数是否更新;useEffect
, 用useMemo计算结果做为依赖,判断是否发起请求;如今留下的惟一疑问就是,发布订阅怎么实现的,看一下代码:
// query 示例其中的两个核心方法: startQuery() { const { url, body, forceUpdate, result } = this; if (this.status > STATUS.fetch) { return; } // 状态反转为请求中 this.status = STATUS.fetch; result.loading = true; // 发起请求 this.request(url, body).then((data) => { // 更新结果 result.data = data; result.loading = false; this.status = STATUS.success; // 更新订阅 forceUpdate(); }).catch((error) => { this.status = STATUS.error; result.error = error; result.loading = false; forceUpdate(); }); } execute() { // skip:是否禁用查询 const { result, options: { skip = false } } = this; !skip && this.startQuery(); // 当skip 为true 时,说明没有查询结果,因此不能用上次的查询结果来作过渡 return Object.assign({ error: undefined, data: undefined, loading: !skip, forceUpdate: this.forceQuery, }, skip ? {} : result); }
是否是有种恍然大悟,并无什么发布订阅的具体实现,彻底依赖于Promise
对象隐式的发布订阅
,而forceUpdate的实现则依赖于useReducer
:
const [tick, forceUpdate] = useReducer(x => x + 1, 0);
从去年用Graphql
写完本身的博客,发现Apollo的useQuery这个hooks彩蛋,就一直有想法去实现一个useRequest hooks,在去年的一次需求推动中,强迫本身去编写了这个组件。收获仍是很大的,在咱们团队中已经有必定尝试,固然还有很大的拓展空间。从这个组件的编写历程,我也再一次体验了开手动挡,司机经验的重要性,拿我本身挖到的一个坑举例:
cleanup() { this.result.data = undefined; this.result.loading = false; this.result.error = undefined; }
上面一段代码,是每次请求完成后,页面更新后, 有一个useEffect反作用会执行query.cleanup()
来保证请求示例回到初始状态。但就这样一段代码,引发了极大的性能问题:
事件唤起查询时,页面会抖动,开始我觉得是我写的hooks的组件不如
react-redux
那么多性能优化,其实当时在猜疑是否是hooks的性能问题。
但后面经过应用chrome performance,观测到其抖动,是因为this.result.data = undefined
形成的,用一个示意图表示:
大概意思就是,若是当前页面有数据,再次发起查询时,列表除了当前更新为loading状态,当前10天数据也会被清除,至关于列表作了一次diff并render
;请求完成后,loading状态消失,更新列表,又作了一次diff并render.因此会形成抖动。后面完善代码后,就和下面正常的阶跃曲线同样,只有一次阶跃,列表实际只会作一次render.
这次实现是基于团队现有的http库来作的,这个库的原理在之前的一篇文章讲过:边看边写:基于Fetch仿洋葱模型写一个Http构造类
若是你感兴趣,能够在个人github看到:
没啥想总结的,愿疫情早日过去。愿口罩早日摘下,愿火锅早日成为生活的平常。
对了,Antd4.0已经到来,Form表单基本被重写,这意味着我组件库Antd-doddle, 又得作一次大的升级了!!!!cd