前言 

此篇文章主要是因为在看Virtual DOM(虚拟DOM)的时候看到的主要讲的是实现Virtual

Dom 的diff算法,原文地址:https://calendar.perfplanet.com/2013/diff/

译文

React是一个Facebok开发的用于构建用户界面的JavaScript库。它的设计从根本上考虑到性能。在这篇文章中,作者将演示diff算法和渲染在React如何工作,以便您可以优化您自己的应用程序。

Diff 算法

在我们了解实现细节之前,了解React的工作原理非常重要。

 varMyComponent=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。重要的是要理解render的结果不是一个实际的DOM节点。这些只是轻量级的JavaScript对象。我们称之为虚拟DOM。

React将使用这个表示(译者注:虚拟DOM)来尝试找到从前一个渲染到下一个渲染的最少步骤数。例如,如果我们加载<MyComponent first={true} />,替换它<MyComponent first={false} />,然后卸载它,这里是DOM指令结果:

没有第一

  • 创建节点: <div className="first"><span>A Span</span></div>

第一到第二

  • 替换属性:className="first"className="second"
  • 替换节点:<span>A Span</span><p>A Paragraph</p>

最好的

  • 删除节点: <div className="second"><p>A Paragraph</p></div>

逐级

找到两个任意树之间的diff是一个O(n ^ 3)问题。你可以想象,这是不适合我们的实际使用情况。React使用简单而强大的启发式方法在O(n)中找到非常好的近似。

React只尝试逐级协调树。这大大降低了复杂性,并不是一个大损失,因为在Web应用程序中将组件移动到树中的不同级别是非常罕见的。他们通常只在同级的节点间移动。

列表

假设我们有一个组件,在一次迭代渲染5个组件,然后下一次操作是插入一个新的组件在列表的中间。

默认情况下,React将上一个列表的第一个组件与下一个列表的第一个组件相关联,以此类推。您可以提供一个key属性,以帮助React找出映射。在实践中,找出每一个孩子节点的唯一key值是很容易的。

组件

React应用程序通常由许多用户定义的组件组成,最终变成主要由divs 组成的树。这个附加信息被diff算法考虑,因为React将仅匹配具有相同类的组件。

例如,如果一个 <Header>被替换<ExampleBlock>,React将删除<Header>并创建一个<ExampleBlock>。我们不需要花费宝贵的时间来尝试匹配不可能具有任何相似性的两个组件。

事件委托

将事件侦听器附加到DOM节点是非常缓慢和内存消耗。相反,React实现了一种名为“事件委托”的流行技术。React更进一步,重新实现一个符合W3C标准的事件系统。这意味着Internet Explorer 8事件处理错误是过去的事情,所有的事件名称在不同的浏览器中是一致的。

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

相反,我们利用每一个React组件都有一个层次编码的独一无二的id值得事实。我们可以使用简单的字符串操作来获取所有父级的id。通过将事件侦听器存储在一个hash map中,我们发现它比将它们附加到虚拟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);

浏览器为每个事件和每个侦听器创建一个新的事件对象。这有一个nice的特性 ,您可以保留对事件对象的引用或甚至修改它。然而,这意味着进行大量的内存分配。React在启动时分配这些对象的分发池。每当需要一个事件对象时,它都会从该分发池中重用。这显着减少了垃圾收集。

渲染(render)

批处理

每当你调用setState一个组件时,React会将其标记为脏。在事件循环结束时,React会查看所有脏组件并重新呈现它们。

此批处理意味着在事件循环期间,正好有一次DOM正在更新。这个特性是构建高性能应用程序的关键,但是使用常用的JavaScript极难获得。在React应用程序中,默认情况下会得到它。

子树渲染

setState调用时,组件为其子代重建虚拟DOM。如果你调用setState根元素,那么整个React应用程序将被重新渲染。所有的组件,即使他们没有改变,将render调用他们的方法。这听起来可怕和低效,但在实践中,这工作正常,因为我们不触摸实际的DOM。

首先,我们正在谈论显示用户界面。由于屏幕空间有限,您通常一次显示数百到数千个元素的顺序。JavaScript已经得到足够快的业务逻辑为整个接口是可管理的。

另一个重要的点是,当编写React代码时,通常不会在每次更改时在根节点上调用setState。您在接收到更改事件或上面的几个组件的组件上调用它。你很少去一路到顶部。这意味着更改会本地化到用户交互的位置。

选择性子树渲染

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

 boolean shouldComponentUpdate (object nextProps ,object nextState )

基于组件的上一个和下一个属性/状态,你可以告诉React这个组件没有改变,没有必要重新渲染它。当正确实施时,这可以提供巨大的性能改进。

为了能够使用它,你必须能够比较JavaScript对象。有很多关于这个的issues,比如应该浅层比较还是深层比较;如果它是深层的,我们应该使用不可变的数据结构或做深拷贝。

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

结论

使React快速的技术不是新的。我们已经知道很长时间,触摸DOM是昂贵的,你应该批量写和读操作,事件委派更快...

人们仍然谈论他们,因为在实践中,他们很难在常规JavaScript代码中实现。使React脱颖而出的是所有这些优化默认发生。这使得很难射击自己在脚,使你的应用程序缓慢。

React的性能成本模型也很容易理解:每个setState重新呈现整个子树。如果你想挤出性能,调用setState尽可能低,并使用shouldComponentUpdate来防止重新渲染一个大的子树。

 
 

React的diff算法(译文)的更多相关文章

  1. 深入理解React:diff 算法

    目录 序言 React 的核心思想 传统 diff 算法 React diff 两个假设 三个策略 diff 具体优化 tree diff component diff element diff 小结 ...

  2. React中diff算法的理解

    React中diff算法的理解 diff算法用来计算出Virtual DOM中改变的部分,然后针对该部分进行DOM操作,而不用重新渲染整个页面,渲染整个DOM结构的过程中开销是很大的,需要浏览器对DO ...

  3. react的diff算法与antd中switch组件不更新问题

    问题描述: 现在有个需求,现有一个列表table,里面的数据有启用的也有关闭的,switch组件会根据数据状态展示,同时进行排序,启用数据在前面,未启用的在后面.如图 然后现在需要操作,假如我将第四条 ...

  4. React的Diff算法

    使用React或者RN开发APP如果不知道Diff算法的话简直是说不过去啊.毕竟"知其然,知其所以然"这句老话从远古喊到现代了. 以下内容基本是官网文章的一个总结.压缩.这次要谦虚 ...

  5. 个人对于React的Diff算法的一点疑问(待更新)

    本人对于Diff算法也并未做深入研究,只是大概的看过一些博文了解了些原理,但依然有了如下疑问 : 对于vdom所表示的对象中,若在该oldObj和newObj之间,发现一个元素节点所表示的子对象不见了 ...

  6. React——diff算法

    react的diff算法基于两个假设: 1.不同类型的元素会产生不同的树 2.通过设置key,开发者能够提示那些子组件是稳定的 diff算法 当比较两个树时,react首先会比较两个根节点,接下来具体 ...

  7. react中虚拟dom的diff算法

    .state 数据 .jsx模板 .生成虚拟dom(虚拟DOM就是一个js对象,用它来描述真实DOM) ['div', {id:'abc'}, ['span', {}, 'hello world']] ...

  8. react性能调谐与diff算法

    一个页面其实就相当于是一颗dom树,里面有很多它的子节点,然后你每次去操作一个事件,它都会生成一个虚拟dom,它会跟上一个虚拟dom进行比对,这里运用的算法叫做diff算法,当它找到需要改变的组件的时 ...

  9. diff算法

    diff算法的作用计算出Virtual DOM中真正变化的部分,并只针对该部分进行原生DOM操作,而非重新渲染整个页面. 传统diff算法 通过循环递归对节点进行依次对比,算法复杂度达到 O(n^3) ...

随机推荐

  1. Spring Cloud开发人员如何解决服务冲突和实例乱窜?(IP实现方案)

    一.背景 在我上一篇文章<Spring Cloud开发人员如何解决服务冲突和实例乱窜?>中提到使用服务的元数据来实现隔离和路由,有朋友问到能不能直接通过IP来实现?本文就和大家一起来讨论一 ...

  2. 【selenium】- webdriver常见元素定位(上)

    本文由小编根据慕课网视频亲自整理,转载请注明出处和作者. 1. 元素的定位 2.By.id 打开Firefox,打开百度首页,右键点击选择“使用Firebug”查看元素. 点击红框内的按钮,将鼠标指针 ...

  3. 2019nc#9

    题号 标题 已通过代码 题解/讨论 通过率 团队的状态 A The power of Fibonacci 点击查看 进入讨论 69/227 未通过 B Quadratic equation 点击查看 ...

  4. CodeForces Round #514 (div2)

    A:Cashier 题意:问可以休息多少次. 代码: #include<bits/stdc++.h> using namespace std; #define Fopen freopen( ...

  5. 杭电多校第四场 Problem K. Expression in Memories 思维模拟

    Problem K. Expression in Memories Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262 ...

  6. hdu 2767 Proving Equivalences(tarjan缩点)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2767 题意:问最少加多少边可以让所有点都相互连通. 题解:如果强连通分量就1个直接输出0,否者输出入度 ...

  7. window对象,BOM,window事件,延时器,DOM

    01.定时器补充 function fn(){    console.log(1);}setInterval("fn()",100);  //定时器调用匿名函数/*   funct ...

  8. Charles安装windows篇

    简介 Charles是一款非常好用的网络抓包工具,类似fiddle抓包工具,当然也可以理解为一款HTTP代理服务器.HTTP监视器.反向代理服务器等. 二.官网下载 地址:https://www.ch ...

  9. python每日经典算法题5(基础题)+1(中难题)

    现在,越来越多的公司面试以及考验面试对算法要求都提高了一个层次,从现在,我讲每日抽出时间进行5+1算法题讲解,5是指基础题,1是指1道中等偏难.希望能够让大家熟练掌握python的语法结构已经一些高级 ...

  10. Go语言标准库之fmt

    fmt标准库是我们在学习Go语言过程中接触最早最频繁的一个了,本文介绍了fmtb包的一些常用函数. fmt fmt包实现了类似C语言printf和scanf的格式化I/O.主要分为向外输出内容和获取输 ...