一、单个React组件的性能优化
React利用Virtual DOM来提高渲染性能,虽然这能将每次DOM操作量减少到最小,计算和比较Virtual DOM依然是一个复杂的计算过程。如果能够在计算Virtual DOM之前就能判断渲染结果不会有变化,那样可以干脆不要进行Virtual DOM计算和比较,速度就会更快。
发现浪费的渲染时间
在Chrome浏览器中安装React Perf扩展,步骤省略(属于操作部分)
性能优化的时机
“我们应该忘记忽略很小的性能优化,可以说97%的情况下,过早的优化是万恶之源,而我们应该关心对性能影响最关键的那另外3%的代码” --高德纳
对于合并多个字符串,怎样合并,使用什么方法合并不大可能对整个应用造成关键的性能影响,这就是高纳德所说的97%的情况,而选择用什么样的方式去定义组件的接口,如何定义state到prop的转变,使用什么样的算法来比对Virtual DOM,这些决定对性能和架构的影响是巨大的,就是那关键的3%。
二、多个React组件的性能优化
和单个组件的生命周期一样,React组件也要考虑3个阶段:装载阶段、更新阶段、卸载阶段。其中,装载阶段基本没什么可以优化的空间,因为这部分工作没有什么可以省略的。而卸载阶段,只有一个生命周期函数componentWillUnmount,这个函数做的事情只是清理componentDidMount添加的事件处理监听等收尾工作,做的事情要比装载过程少很多,所以也没什么可以优化的空间。所以值得关注的过程,只剩下更新过程。
React的调和过程
React在更新阶段,很巧妙的对比原有的Virtual DOM和新生成的Virtual DOM(存在于内存中),找出两者的不同,根据不同修改DOM树,这样只需做最小的必要改动。
React在更新中找不同的过程,就叫做调和(Reconciliation)。
React实际采用的算法的时间复杂度是O(N)。React的Reconciliation算法并不复杂,当React要对比两个Virtual DOM的树形结构的时候,从根节点开始递归往下对比,在树形结构上,每个节点都可以看做这个节点以下子树部分的根节点,所以其实这个对比算法可以从Virtual DOM上的任何一个节点开始执行。
React首先检查两个根节点的类型是否相同,根据相同或者不同有不同处理方式。
(1)节点类型不同的情况
这时可以直接认为原来的树形结构已经没用,需要重新构建新的DOM树,原有树形上的React组件会经历“卸载”的生命周期。这时,componentWillUnmount的方法会被调用,取而代之的组件则会经历装载过程的生命周期,组件的componentWillMount、render和componentDidMount方法会被依次调用。
(2)节点类型相同的情况
这时React就会认为原来的根节点只需要更新,不必将其卸载,也不会引发根节点的重新装载。
这时,有必要区分一下节点的类型,节点的类型可以分为两类:一类是DOM元素类型,对应的就是HTML直接支持的元素类型,比如<div />,<span />和<p />;另一类是React组件,也就是利用React库定制的类型。
三、用reselect提高数据获取性能
两阶段选择过程
reselect库的工作原理:只要相关状态没有改变,那就直接使用上一次的缓存结果。
reselect库被用来创造“选择器”,即接受state作为参数的函数,这个选择器函数返回的数据就是我们某个mapStateToProps需要的结果。
reselect认为一个选择器的工作可以分为两个部分,把一个计算步骤分为两个步骤:
(1)从输入参数state抽取第一层结果,将这第一层结果和之前抽取的第一层结果做比较,如果发现完全相同,就没有必要进行第二部分运算了,选择器直接把之前第二部分的运算结果返回就好了。注意:这一部分做的比较,就是JavaScript的===操作符比较,如果第一层结果是对象的话,只有是同一对象才会被认为是相同。
(2)根据第一层结果计算出选择器需要返回的最终结果。
显然,每次选择器函数被调用时,步骤一都会被执行,但步骤一的结果被用来判断是否可以使用缓存的结果,所以并不是每次都会调用步骤二的运算。
剩下的事情就是确定选择器步骤一和步骤二分别进行什么运算。原则很简单,步骤一运算因为每次选择器都要使用,所以一定要快,运算要非常简单,最好就是一个映射运算,通常就只是从state参数中得到某个字段的引用就足够,把剩下来的重活累活都交给步骤二去做。
在TodoList的具体例子中,todos和filter的值直接决定了应该显示什么样的待办事项,所以,步骤一是获取todos和filter的值,步骤二是根据这两个值进行计算。
使用reselect需要安装对应的npm包:
npm install --save reselect
reselect提供了创造选择器的createSelector函数,这是一个高阶函数,也就是接受函数为参数来产生一个新函数的函数。
第一个参数是一个函数数组,每个元素代表了选择器步骤一需要做的映射计算,这里我们提供了两个函数getFilter和getTodos,代码如下:
const getFilter = (state)=>state.filter;
const getTodos = (state) = >state.todos;
createSelector函数的第二个参数代表步骤二的计算过程,参数为第一个参数的输出结果。
现在,可以在TodoList模块中改用新定义的选择器来获取待办事项数据了:
import {selectVisibleTodos} from '../selector.js'
const mapStateToProps = (state) =>{
return {
todos : selectVisibleTodos(state)
}
}
Redux要求每个reducer不能修改state状态,如果要返回一个新的状态,就必须返回一个新的对象。这样,如果state状态树上的某个节点没有变化,那我们可以认为这个节点下的数据没有改变,应用在reselect中,步骤一的运算就可以确定使用缓存的数据结果。
虽然reselect的createSelector创造的选择器并不是一个纯函数,但是createSelector接受的所有函数参数都是纯函数,虽然选择器有“记忆”这个副作用,但只要输入参数state没有变化,产生的结果也就没有变化,表现得却类似于一个纯函数。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。