React Diff 算法

更新日期: 2017-11-27阅读: 4k标签: diff

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

来源:原文链接 翻译链接 


链接: https://fly63.com/article/detial/179

浅析vue2.0的diff算法

如果不了解virtual dom,要理解diff的过程是比较困难的。虚拟dom对应的是真实dom, 使用document.CreateElement 和 document.CreateTextNode创建的就是真实节点。vue2.0才开始使用了virtual dom,有向react靠拢的意思。

简述dom diff原理

关于react的虚拟dom以及每次渲染更新的dom diff,网上文章很多。但是我一直信奉一个原则,即:但凡复杂的知识,理解之后都只需要记忆简单的东西,而想简单、精确描述一个复杂知识,是极困难的事。

传统diff、react优化diff、vue优化diff

传统diff计算两颗树形结构差异并进行转换,传统diff算法是这样做的:循环递归每一个节点;传统diff算法复杂度达到O(n^3 )这意味着1000个节点就要进行数10亿次的比较,这是非常消耗性能的

React 中 Virtual DOM 与 Diffing 算法的关系

Virtual DOM 是一种编程理念。UI 信息被特定语言描述并保存到内存中,再通过特定的库,例如 ReactDOM 与真实的 DOM 同步信息。这一过程成为 协调 (Reconciliation)。上述只是 协调算法

Vue2.x的diff算法记录

为什么在Vue3.0都已经出来这么久了我还要写这篇文章,因为目前自己还在阅读Vue2.x的源码,感觉有所悟。作为一个刚毕业的新人,对Vue框架的整体设计和架构突然有了一点认知,所以才没头没尾地突然写下了diff算法。

深入理解React Diff算法

fiber上的updateQueue经过React的一番计算之后,这个fiber已经有了新的状态,也就是state,对于类组件来说,state是在render函数里被使用的,既然已经得到了新的state

虚拟 DOM 与 Diff 算法的实现原理

Vue 源码中虚拟 DOM 与 Diff 算法的实现借鉴了 snabbdom 这个库,snabbdom 是一个虚拟 DOM 库,它专注于简单,模块化,强大的功能和性能。要彻底明白虚拟 DOM 与 Diff 算法就得分析 snabbdom 这个库到底做了什么?

手写一个虚拟DOM库,彻底让你理解diff算法

所谓虚拟DOM就是用js对象来描述真实DOM,它相对于原生DOM更加轻量,因为真正的DOM对象附带有非常多的属性,另外配合虚拟DOM的diff算法,能以最少的操作来更新DOM,除此之外

详解虚拟DOM与Diff算法

那么需要真实的操作DOM100w次,触发了回流100w次。每次DOM的更新都会按照流程进行无差别的真实dom的更新。所以造成了很大的性能浪费。如果循环里面是复杂的操作,频繁触发回流与重绘

浅析snabbdom中vnode和diff算法

目前前端使用最多的就是 vue 或 react 了,我们在学习这两个框架的过程中,总有一个绕不开的话题:vnode,也就是虚拟 dom。什么是虚拟 DOM,引用一段 vue 官方的解释就是:

点击更多...

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