在前端面试中,只要涉及到React框架,diff算法便是我们绕不开的话题,这次让我们来系统性的梳理diff算法,不再被这个知识点所困扰。


什么是diff算法?


在了解diff算法之前,我们要直到diff算法并非React独家首创,但是React针对diff算法做了自己的优化,使得diff算法可以帮助我们计算出Virtual Dom中真正变化的部分,并只针对该部分进行实际的DOM操作,而非渲染整个页面,从而保证了每次操作后页面的高效渲染。


传统diff算法


要想了解React的diff算法,我们首先要知道传统的diff算法是如何设计并实现的。


传统diff算法的时间复杂度


传统diff算法的时间复杂度是O(N^3),其中N是树中节点的总数,这样的时间复杂度意味着如果要展示1000个节点,就要执行多达十亿次的比较,这种指数型的性能消耗对于前段渲染场景来说代价太高了。



React只有将diff算法进行改进,才有可能满足前端渲染所要求的的性能。



之所以传统diff算法的时间复杂度是O(N3)是因为两个二叉树的每一个节点进行两两对比的时间复杂度是O(N2),此时如果继续进行树的编辑操作(修改、删除)等还需要O(N)的时间复杂度,所以总的时间复杂度是O(N^3)。


React优化后的diff算法


React通过自己的优化,将O(N^3)的时间复杂度降到了O(N)。


React diff的三个前提策略


  1. Web UI中DOM节点跨层级的移动操作特别少,可以忽略不计。
  2. 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构。
  3. 对于同一层级的一组子节点,它们可以通过唯一id进行区分。


React进行tree diff、component diff和element diff进行算法优化是基于上面三个前提策略。事实证明上面的三个前提策略是非常有效的。


核心策略1:tree diff(树策略)


对树进行分层比较,两棵树只会对同一层次的节点进行比较。


核心策略2:component diff(组件策略)


React是基于组件构建应用的,对于组件间的比较采用下面的方式:



  • 如果是同一类型的组件,按照原策略继续比较虚拟DOM树,对于同一类型的组件,有可能其虚拟DOM树并没有任何变化,如果能够在比较之前准确的知道这一点,可以节省大量的运算时间,所以React向用户提供了shouldComponentUpdate()来判断该组件是否需要进行diff。
  • 如果不是同一类型的组件,则将该组件判断为dirty component,从而替换掉整个组件下面的所有子节点。

核心策略3:element diff(元素策略)


对于同一层级的一组子节点,通过唯一id进行区分。