当前位置: 首页 > Web前端 > HTML

React内部令人困惑的性能优化策略

时间:2023-03-28 00:16:54 HTML

大家好,我是Kason。与Vue可以基于模板进行编译时性能优化相比,React作为一个完整的运行时库,只能在运行时寻求性能优化。这些优化大部分对开发者来说是察觉不到的,但是在优化项目的性能时却常常让开发者感到困惑。例如下面的代码:functionApp{const[num,updateNum]=useState(0);console.log('Apprender',num);useEffect(()=>{setInterval(()=>{updateNum(1);},1000)},[])return;}functionChild(){console.log('childrender');returnchild;}挂载App组件后会打印多少条信息?本文就此Demo讲解了React内部的性能优化策略。在线Demo地址欢迎加入人类优质前端框架群,一起享受性能优化的效果。如果不考虑优化策略,代码运行逻辑如下:App组件第一次渲染,打印Apprender0subcomponentChild第一次渲染,打印childrender1000ms后,setInterval回调触发,执行updateNum(1)Appcomponentrenderagain,printApprender1childcomponentChildrenderagain,printchildrenderevery1000ms,repeatsteps3~5其实我们会发现重复3~5不会产生任何的结果这里显然有优化的空间。针对这种情况,React确实做了优化。上面的Demo会依次打印:Apprender0childrenderApprender1childrenderApprender1这里比较迷惑的一点是:为什么num从0变为1后Apprender1执行了两次,而childrender只执行了一次?下面,我们从理论和实践的角度解释上述原因。性能优化理论在useState文档中提到了一个名词:bailout。他指的是:当useState更新的状态与当前状态相同时(相对于Object.is),React不会渲染该组件的后代组件。注意:命中bailout后,当前组件可能仍会呈现,但其后代不会呈现。这是因为,在大多数情况下,只有当当前组件被渲染时,useState才会被执行,并且可以计算状态,然后与当前状态进行比较。就我们的demo而言,num只能在Apprender和useState执行后计算:functionApp{//num只能在useState执行后计算const[num,updateNum]=useState(0);//...省略}在useStatenotbailoutwhenstatedoesnotchange#14994中,Dan也重申了这一点。所以从理论上来说,在我们的Demo中,num从0变为1后,childrender只执行一次是可以理解的,因为App打了bailout,它的子组件Child不会render。但是bailout只是针对目标组件的后代组件,那为什么对于目标组件App,Apprender1执行了两次之后就不再执行了呢?实际的性能优化策略更为复杂。实际性能优化策略React的工作流程可以简单概括如下:交互(如点击事件、useEffect)触发组件树渲染的更新。刚才说的bailout发生在第2步:组件树开始渲染后,命中bailout组件的后代不会渲染。其实还有更高级的优化策略:当第1步触发更新时,发现状态没有变化,根本不会继续第2步。来自我们的演示:functionApp{const[num,updateNum]=useState(0);console.log('Apprender',num);useEffect(()=>{setInterval(()=>{updateNum(1);},1000)},[])return;}一般情况下执行updateNum(1)触发更新。直到Apprender和useState执行完才会计算新的num,然后和当前的num比较,判断是否bailout命中。如果执行了updateNum(1),则立即计算出新的num,然后与当前的num比较,如果相等则不渲染组件树。这种提前状态计算时机的策略称为eagerState(急切状态)。总结总之,我们的演示是混合这两种优化策略的结果:Apprender0(missstrategy)childrenderApprender1(missstrategy)childrenderApprender1(hitbailout)(hiteagerState)(HiteagerState)...bailout的具体实现请参考React组件何时渲染。限于篇幅,eagerState的实现细节将另文讨论。