React的“差异”启发式算法背后的动机是什么?

时间:2019-05-13 10:13:53

标签: reactjs algorithm

我的问题是关于Motivation,用于实现启发式 O(n)算法

  

对于以下算法问题,有一些通用的解决方案   生成将一棵树转换为最小数量的操作   另一个。但是,state of the art algorithms的复杂性在于   O(n ^ 3)的顺序,其中n是树中元素的数量。

  • 为什么将一棵树转换为另一棵树的复杂度为O(n ^ 3)?
  

如果我们在React中使用此功能,则需要显示1000个元素   十亿次比较的顺序。这太昂贵了。   相反,React基于两个假设实现了一种启发式O(n)算法:

     
      
  • 两个不同类型的元素将产生不同的树。
  •   
  • 开发人员可以使用关键道具提示哪些子元素在不同的渲染中可能稳定。
  •   
  • 您能否详细说明React的实现中的heuristic是什么?
  • 假设是否使它在一般情况下为O(n)?

3 个答案:

答案 0 :(得分:0)

  • 只有基于假设的转换
  
      
  • 两个不同类型的元素将产生不同的树。
  •   
  • 开发人员可以使用关键道具提示哪些子元素在不同的渲染中可能稳定。
  •   

如果键未更改或未添加新元素,则不会重新渲染整个树

  • 此管理基于经验假设,因此是启发式

答案 1 :(得分:0)

当前最先进的扩散算法的复杂度为O(n^3)(n个节点),无法找到将一棵树转化为另一棵树所需的最少数量的变换操作树。但是,这种复杂性(如React docs mention可能会很高,在通常情况下是不需要的。

这就是为什么React使用启发式算法平均要花费O(n)(线性时间)的原因。

  
      
  1. 两个不同类型的元素将产生不同的树。
  2.   
  3. 开发人员可以使用关键道具提示哪些子元素在不同的渲染中可能稳定。
  4.   

成为启发式意味着在某些情况下diff可能会产生比所需的转换更多的转换(通常不是最佳的),但是在两种算法(最佳和启发式)的通常和常用情况下它可能是最佳的可以产生完全相同的结果(启发式方法花费的时间更少),或者两种算法之间的差异对性能的影响最小。

PS:

  

为什么将一棵树转换为另一棵树的复杂度为O(n ^ 3)?

为了回答这个问题,必须研究最新的算法。但是总的来说,答案是必须进行许多比较(在节点及其子节点之间),以找到所需转换的最小数量

答案 2 :(得分:0)

React的diff算法按原样运行是有充分的理由的,但是记录在案的“动机”并没有真正意义上的真实性。

首先,虽然最佳“树差异”确实需要O(N 3 )时间,但是“树差异”算法并不是React实际工作的唯一最佳选择,并且实际上,react的渲染过程根本不合适。这主要是因为在最坏的情况下,渲染反应组件会生成反应元素的 list (不是树),需要与预先存在的 list 相匹配组件。

执行diff时没有新树,因为新列表需要在呈现新元素的子代之前与 相匹配。实际上,diff的结果需要决定是否重新渲染孩子。

所以...为了匹配这些列表,我们可以将React diff与标准的Longest-Common-Subsequence算法进行比较,该算法是O(N 2 )算法。这仍然很慢,并且需要对性能进行论证。如果LCS与React diff一样快,那么它肯定会在渲染过程中占有一席之地。

但是,不仅LCS速度慢,而且做不正确的事情。当React将新元素的列表与旧树匹配时,它将决定每个元素是一个新组件,还是只是对现有组件的prop更新。 LCS可以找到元素类型的最大可能匹配,但是最大可能的匹配并不一定是开发人员想要的。

因此,LCS(或树差异,如果您真的想推动问题)的问题不仅在于它很慢,而且还很慢 它提供的答案仍然是只是出于开发人员的意图而已。慢速算法仍然会犯错,这是不值得的。

不过,React开发人员可能会使用许多其他快速算法,这些算法在大多数情况下会更准确,但是问题是“值得吗?”通常,答案是“否”,因为没有一种算法能够很好地猜测开发者的意图,并且猜测开发者的意图实际上是不必要

对于开发人员来说,重要的是要使他的新元素与他先前存在的组件正确匹配,从而不必重新渲染它们,那么开发人员应确保 是这种情况。这很容易-他在提供列表时只需要提供关键道具即可。开发人员在呈现列表时应该几乎总是这样做,这样组件匹配就可以完美,而且他们不必为任何猜测而满足。

如果您不将关键道具放在需要明确匹配的地方,React就会发出警告,这比更好的区分要有用得多。当您看到它时,应该通过添加适当的关键道具来修复组件,然后进行匹配就可以了,并且没有其他算法可以更好地处理写得不好的组件。