React Diff 算法

时间: 2017-11-27阅读: 1010标签: 算法

React 是 facebook 出的一个前端框架. 设计的关键处就是性能问题。在本文中,我主要是介绍 Diff 算法以及 React 渲染 ,这样你可以更好的优化你的应用程序。

Diff 算法

再介绍算法之前,我们先来了解一下 react 是怎么工作的。

var MyComponent = React.createClass({ 
    render: function() {
        if (this.props.first) {
            return <div className="first"><span>A Span</span></div>;
        } else { 
            return <div className="second"><p>A Paragraph</p></div>; 
        } }
    });

在你构思着要UI 的时候。要知道最重要的一点是:渲染的结果不是一个真实 DOM 。 只是个普通的 JavaScript 对象. 我们就把它叫做虚拟 DOM.

React 将使用这个方法试着用最快捷的步骤从前一个渲染到下一个。例如, 如果我们挂载 , replace it with, 然后卸载它, DOM 的结果会是这种情况:

None to first

  • Create node: A Span

First to second

  • Replace attribute: className="first" by className="second"
  • Replace node: A Span by A Paragraph

Second to none

  • Remove node: A Paragraph

Level by Level

可以看到,在任意两棵树上找到最少修改次数为 O(n^3)。 这不是我们想要的. React 就使用了一种简单高效的方式来处理这个修改,为 O(n).

React 只是一级一级的调用这棵树。 在影响极小的性能代价下大大的降低来复杂度,在 Web 应用中移动不同的 DOM 树是及其罕见的。 通常只会在子节点上进行横向移动。


List

假设我们有一个组件,一次渲染五个组件并在下一次渲染时候插入在其中插入一个新组件。这样很难比较这两个列表中组件之间的映射关系

默认情况下,React 会把前一个列表组件和下一个列表组件相关联起来。你可以通过添加 key的方式,去帮助 React 找到映射。在实际中,这样子元素就会容易的被找出来。


Components

通常,一个 React App 会有很多用户自己定义的 div 元素,这样元素构成一颗很复杂的树。由于 React 只会比较有同一个类的组件,因此 diff 算法会带一些额外的组件信息。

例如假如是 is replaced by an, React 将删除标题组件并创建一个新的组件。 我们不需要太多的时间来匹配两个不太可能有相似之处的组件。


Event Delegation

在 DOM 节点上进行事件监听会使程序变得缓慢而且会耗费大量的内存。 因此,React 就采用了事件委托的方式。React 更进一步的实现符合 W3C 标准的事件。 Internet Explorer 8 的事件错误已经成为历史,所有事件名称在浏览器中都是一致的。

让我解释一下它是如何实现的。 单个事件侦听器附加到文档的根目录。 当事件被触发时,浏览器给我们目标 DOM 节点。 为了通过 DOM 传播事件,React 将不会在虚拟 DOM 上迭代。

相反,我们使用每个 Reac t组件都有唯一的 ID。 我们可以使用简单的字符串操作来获得所有父组件的 ID。 通过将事件侦听器存储在哈希映射中,我们发现它比将它们附加到虚拟 DOM 更好。 以下是通过虚拟DOM分派事件时发生的情况的示例。

// dispatchEvent('click', 'a.b.c', event) clickCaptureListeners['a'](event); clickCaptureListeners['a.b'](event); clickCaptureListeners['a.b.c'](event); clickBubbleListeners['a.b.c'](event); clickBubbleListeners['a.b'](event); clickBubbleListeners['a'](event);

浏览器为每个事件和每个侦听器创建一个新的事件对象。 这有很好的性能,你可以保留事件对象的引用,甚至修改它。 但是,这意味着要进行大量的内存分配。 在启动时反应分配这些对象的池。 每当需要事件对象时,都会从该池中重用。 这大大减少了垃圾收集。

Rendering

Batching

每当你调用一个组件的 setState 时,React 就会把它标记为脏元素。 在事件循环结束时,React 查看所有有脏元素的组件并重新渲染它们。

这个批处理意味着在一个事件循环期间,DOM 正在被更新。 这个属性是构建高性能应用程序的关键。 在React应用程序中,默认情况下会使用它。


Sub-tree Rendering

当调用setState时,组件会重新建立其子组件虚拟 DOM。 如果你在根元素上调用setState,那么整个React应用程序就会被重新渲染。 所有的组件,即使它们没有改变,都会调用它的“渲染”方法。 这听起来可怕,效率低下,但在实践中,这影响也不大,因为我们并没有触及到实际的DOM。。。。。(其实还是蛮大的啊,有些组件会多次渲染啊,shouldUpdate 优化)

首先,我们正在讨论显示用户界面。 由于屏幕空间有限,通常一次需要显示数百到数千个元素的订单。 幸好 JavaScript 已经有足够快的业务反应,使得整个界面处于可管理状态。

另一个重要的一点是,在编写 React 代码时,最好每次改变时都不要在根节点上调用 setState。 您可以在接收到更改事件的组件或它的父组件上调用。你很少会在最顶端去处处理逻辑,这就意味着渲染只会发生在组件自己的节点上。


Selective Sub-tree Rendering

最后,你有可能阻止一些子树重新渲染。 如果您在组件上实现以下方法:

boolean shouldComponentUpdate(object nextProps, object nextState)

基于组件的前一个和下一个状态,你可以告诉 React 这个组件没有改变,没有必要重新渲染它。 如果执行得当,这可以给你巨大的性能改进。(还是应该从根本上去避免组件的渲染问题。。。把组件当作最好一个组件写。。。。。)

为了能够使用它,你必须要比较 JavaScript 的对象。 如果浅比较的话,就会有很多问题提出来, 如果深的话我们应该使用不可变的数据结构或者做更深层的拷贝。

而且你要记住,这个函数会一直被调用,所以你要确保计算花费的时间比启发式计算所需的时间少,而不是渲染组件的时间, 渲染不是严格需要的。.


Conclusion

我们已经知道很长一段时间触摸DOM是昂贵的,你应该批量写和读操作,事件委托更快...

人们仍然在谈论他们,因为在实践中,他们很难在普通的JavaScript代码中实现。 React的突出之处在于所有这些优化都是默认进行的。 这使得在脚下拍摄自己很难,并使您的应用程序变慢。

React 的性能优化问题也很容易理解:每个setState重新渲染整个子树。 如果要压缩性能,请尽可能调用 setState,并使用shouldComponentUpdate 来防止重新渲染大型子树。

来源:原文链接 翻译链接 


用 JavaScript 实现链表

单链表是表示一系列节点的数据结构,其中每个节点指向链表中的下一个节点。 相反,双向链表具有指向其前后元素的节点。与数组不同,链表不提供对链表表中特定索引访问。

JavaScript数据结构与算法-String

给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。主要就是用到了数组的 split 、 reverse 、 join 、 map 方法,原理:就是把字符串变成数组

Js实现插入排序

插入排序是一种非常简单的算法,最适合大部分已经被排好序的数据。在开始之前,通过可视化演示算法如何运作一个好主意。你可以参考前面的动画来了解插入排序的工作原理。

最短编辑距离

1.第一行表示从ME到空字符所要删除的字符的所以情况 2.第一列表示从空字符到MY所需要插入字符的所有情况 3.斜箭头表示相同字符不需要替换,不相同字符所有替换次数的所有情况

轻松搞定时间复杂度

通过学习本文,你可以掌握以下三点内容:为什么需要时间复杂度;时间复杂度怎么表示;怎样分析一段代码的时间复杂度,相信认真阅读过本文,面对一些常见的算法复杂度分析,一定会游刃有余

js实现快速排序算法的2种方式

通过两个for循环,每一次对比相邻两个数据的大小,小的排在前面,如果前面的数据比后面的大就交换这两个数的位置,这个方法就是比较次数太多了,效率比较低。

js生成32位uuid算法总汇_js 如何生成uuid?

GUID是一种由算法生成的二进制长度为128位的数字标识符。GUID 的格式为“xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”,其中的 x 是 0-9 或 a-f 范围内的一个32位十六进制数。在理想情况下,任何计算机和计算机集群都不会生成两个相同的GUID。

Js加密算法_Base64

首先有个小Tips,1个字母字符 = 1个字节(byte) = 8位(bit),这是表示单位.Base64是网络上最常见的用于传输 8bit 的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。

动态规划解题思路

算法能力就是程序员的内力,内力强者对编程利剑的把控能力就更强。动态规划就是,通过递推的方式,由最基本的答案推导出更复杂答案的方法,直到找到最终问题的解。

Js哈希摘要算法

最近在看一些NPM库的时候总是看到各种哈希签名算法,之前工作中也有用到过签名算法,但并没有深入理解过其中的原理,于是找了点资料稍微了解了一下,总结了这篇文章。

fly63.com版权所有,内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权,请与小编联系!情况属实本人将予以删除!

广告赞助文章投稿关于web前端网站点搜索站长推荐网站地图站长QQ:522607023

小程序专栏: 土味情话心理测试脑筋急转弯幽默笑话段子句子语录成语大全