当前位置:首页 > reactweb > 正文

react diff算法详解

最近要写PPT做小组类分享,就看了下原文的react diff算法;看懂了一个大概,不对的地方还靠大家指点;
原文地址:react diff算法原理

首先假设有个component,结构如下:

备注:这里需要理解虚拟DOM还不是真正的DOM;它们只是轻量级的javascript对象;
react为了渲染上面的DOM结构,怎么处理呢?

第一步:
创建节点:

第二步:
替换节点的属性:

替换节点元素:

第三步:
移除节点:

常见的算法情况分析及其特点:

I. 2颗随机树的差异比较:

正常需要O(n^3) 的复杂度;在react中如何优化呢?
层层比较,不垮层处理
示意图如下:

react level by level

react level by level


通过类似的处理,就可以将负责度降低到大约为O(n)

II. 如果是列表类的呢?
比如渲染了5个li元素,然后需要在中间插入一个li,怎么办?react会对2个元素的各自第一个component相应进行比较,为每个component增加一个key属性,接下来比较key属性就OK了,示意图如下:

react diff算法的list之间的比较

react diff算法的list之间的比较

III. 如果是不同组件之间的比较呢?
因为react的component是用户自定义的,可能都比较复杂;2者之间没有任何关系;比如第一个是div,而第二个是component;这样react不会进行比较,直接忽略,重新生成即可;示例如下:

react 组件 component之间的比较

react 组件 component之间的比较

IV. 事件代理方法:
DOM节点上面绑定事件其实是很复杂,又慢又费内存的;在react中实现了很普通的event delegation事件代理;不过比W3C更加深入一些;这样就可以解决IE8之前的一些什么浏览器相关的事件差异bug;
而统一为一种处理模型;下面解释下详细的过程:
比如在document的root上面绑定了一个事件;在某个事件被触发之后,常规的浏览器会告诉我们当前的target DOM是什么;浏览器会通过扩散的方式反复的进行搜索DOM树,但是react不会通过类似的方法搜索虚拟DOM数;
react是通过工厂树的方式实现,在树中每个component有一个唯一性的id,我们可以方便的知道该id对应的父元素的id列表。我们发现在hash表中存储event元素的方式比在虚拟DOM中绑定事件的方式要方便很多;示例如下:

在常规的浏览器,这样的方法有其便利性:它会给每个元素增加一个event事件,可以方便的对每个元素进行修改查询;不过这样有个很大的毛病,需要大量的内存分配进行数据的存储;而react的方式不同,
它专门有个一个池子,用来存储DOM和事件对象;什么时候需要这个event对象的时候,就直接从池子中取即可;这样能大大的减少GC.

渲染相关的优化:
你再对一个component进行setState操作的时候,react会将它标记为dirty;在事件循环结束后,react会rerender所有标示为dirty的元素;这样react会清理它所有的子元素;
如果document的root元素被标示为dirty,这样会导致整个document需要重新渲染;不过由于是虚拟DOM,所以总体还是比较快的;不过我们程序员很少会直接在某些局部的component需要更新的时候,
设置root需要重新渲染。这样带来的好处,意味着变化只是在某些分支树的局部;没有涉及到根root的变化渲染;示例如下:

react diff算法计算local 局部数变化时候的示意图;

react diff算法计算local 局部数变化时候的示意图;

我们在程序中可以设置一些参数,可以阻止某些局部树进行重新的渲染;比如:

你可以通过它的上一个,或者下一个组件的props或者state属性告诉react,这个分支树不需要重新的渲染;这样大大提高了渲染的效率;为了使用这个方法,我们需要比较js对象的差异;比如涉及到是否是深入比较,还是
浅显比较,涉及到是否有必要进行浅拷贝还是深拷贝用来比较2个js对象;不过有点需要注意的,就是shouldComponentUpdate这个方法会一直被调用;如果比较2个对象耗时过长,比如对象很复杂,那还不如告诉react,请
重新渲染某只局部树,尽管看起来这只树可能没必要进行重新渲染;示例图如下:

react diff算法,用来告示react某些局部分支是否需要重新渲染

react diff算法,用来告示react某些局部分支是否需要重新渲染

最后总结下:
这些让react更快的方法,并不是什么很新的技术,只是在实践中,我们得知操作实体的DOM,其实是很耗性能的,尽管事件代理很快,但是它还是需要大量的进行读和写运算;
react出众的地方在于,它默认就进行了很多的优化,而这些优化将使你逃脱更多的坑,让你的APP更快捷;

react的性能消耗模型很容易理解:每次的setState都会导致react重新去渲染整个分支树;所以如果你想榨干APP的性能,尽量少使用setState,而使用shouldComponentUpdate去阻止react去重新渲染某些较大的分支树;

感谢大家,欢迎留言讨论~

暂无评论

发表评论