React Diff 算法

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

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 来防止重新渲染大型子树。

来源:原文链接 翻译链接 


js多叉树结构的数据,parent表示法转成children表示法

要求是将这个数组转成一个children表示法的对象,即从根节点开始,每个节点存有其子节点数组。转化效果如下(节点必须有个唯一标识符,以下id就是,并且转化前后其他属性保持不变,这里为了显示简洁没有加入其他属性。

js算法_奇偶分割数组

分割一个整数数组,使得奇数在前偶数在后。 比如:给定 [1, 2, 3, 4],返回 [1, 3, 2, 4]。思路分析:排序好的数组:找到奇数进行操作。乱序的数组:使用sort方法进行排序+提取奇数

js字典树算法_Trie树(字典树)实现与应用

字典树又称Trie树、前缀字,单词查找树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构。 字典树是处理字符串常见的一种树形数据结构

js实现:在字符串中找出第一个只出现一次的字符

在给出一个字符串,找出第一个只出现一次的字符。思路分析:可以用对象保存字符出现的次数。将值删除,用 indexOf 查找还有没有相同字符,并查找之前删过的字符,indexOf 的第二个参数,从当前值往后搜索,并查找之前已经查过的字符

JavaScript字符串压缩_js实现字符串压缩

设计一种方法,通过给重复字符计数来进行基本的字符串压缩。例如,字符串 aabcccccaaa 可压缩为 a2b1c5a3 。而如果压缩后的字符数不小于原始的字符数,则返回原始的字符串。 可以假设字符串仅包括a-z的字母

算法工程师的危机

AI概念在2015年起就红得发紫,不论是送外卖,搞团购,卖车,或是推荐莆田医院的,是个公司都会标榜自己是搞人工智能的。在21世纪的第二个十年,计算机专业相关的学生不说自己是搞AI算法的,同学聚会都抬不起头,相亲机会都变少了

js实现合并两个有序数组

给定两个有序整数数组arr1和arr2,将 arr2和arr1进行合并为一个单调非递减的数组,并将其输出。方法一利用循环、方法二直接使用数组的sort方法。

js实现将一个正整数分解质因数

js 把一个正整数分解成若干个质数因子的过程称为分解质因数,在计算机方面,我们可以用一个哈希表来存储这个结果。首先,你需要一个判断是否为质数的方法,然后,利用短除法来分解。

js算法实现_二维数组中的查找

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数

js算法-查找斐波纳契数列中第N个数

所谓的斐波纳契数列是指前2个数是 0 和 1 ,第 i 个数是第 i-1 个数和第i-2 个数的和。下面我们来用js获取菲波那契数列的第N个数为多少:递归、闭包+缓存、直接计算出该数列的值得数组,然后再从数组中取值 、直接使用数学表达式