react-diff算法详解

前言

本文是我阅读《深入React技术栈》所写的总结笔记。如果您觉得本站的markdown代码高亮不友好,建议您查看:原文

reconciliation调和,是react中最为核心的模块,它指的是将virtual dom树转换成actual dom树所耗费的最少操作。他需要进行diff->patch这两个过程。diff是计算virtual dom 树转换成另一棵树进行的最少操作,而patch是将差异更新到真实的dom节点。

diff

tree diff

react为了让运行效率更高,tree diff只对树进行同层对比,不去比较跨层的节点。比如,在树A中第一层有一个节点B,想要将他移动到第二层,但并不会直接移动。而是会在第二层创建节点B,接着创建节点B的子节点,创建节点B的子节点的子节点...,然后再把之前第一层的B节点删除。

<A>         <A>
<B/>        <C>
<C/>        <B/>
</A>   ->   </C>
            </A>
demo

component diff

因为react通过组件化开发,在对比组件差异上也采用上述算法。即,同一层只要出现不是同一类型的组件,就替换该组件的所有子节点。对于同一类型的组件,则通过shouldComponentUpdate去判断是否需要通过diff进行分析。shouldComponentUpdate默认为true。

element diff

element diff主要是根据mountIndexlastIndex进行比较,在确定是否移动 ,mountIndex是A节点在旧节点结合中的位置,lastIndex指访问过的节点,在旧集合中最右的位置,每次遍历都有可能会更新。

算法描述

  1. 遍历新节点集合

  2. 如果出现旧节点集合中有与当前指针所指新节点A相同的节点,则通过对比节点位置进行判断操作,对比mountIndexlastIndex

    如果mountIndex>=lastIndex:不做移动操作。并把lastIndex更新为mountIndex

    如果mountIndex<lastIndex:移动。

  3. 如果新节点集合中有旧节点集合中不存在的节点,添加,更新lastIndex

  4. 最后遍历旧节点集合,如果存在新节点集合上不存在的点,则将其删除。

至于为什么要比较mountIndexlastIndex,是因为要保证当前要进行移动操作的节点一定要比lastIndex小,一是为了节约性能,二是为了使节点排序更有条理,如果不进行比较,看见有相同的节点就移动,整个队列就乱了套了。

Tips:React中有提示说,要尽量避免将最后一个节点移动到第一个节点的操作。就是因为在一上来比较的时候,本来只需要将最后一个节点移动到第一个位置这一个操作。但按照diff算法的逻辑,mountIndex为最大值,所以lastIndex也更新为最大值,第一个节点之后的节点都需要进行移动操作。

不太明白的同学可以参考这篇文章->《React之diff算法》,里面有分步图文描述,更便于理解。

差异队列

在上一小节中,我们已经知道了diff是如何判断哪些节点要移动,哪些节点要删除或新增,这些修改的内容都被加入了差异队列当中。其中这三种节点操作,分别对应三种type:(在这之前通过了flattenChildren方法将子节点扁平化,key值相同的只取最后一个节点)

INSERT_MARKUP: 旧集合中有不存在的组件类型或节点,需要对组件或节点进行插入操作

MOVE_EXISTING: 源码中要对比prevChild===nextChild,即旧集合中有与新集合完全一样的节点,书中说是类型相同且element是可更新的,复用以前的dom节点。

REMOVE_NODE: 旧组件类型在新集合中也存在,但对应的element不同,不能更新和复用。或者旧组件中存在新集合中不存在的,也需要进行删除操作。

源码中有三个函数makeInsertMarkupmakeMovemakeRemove,用来返回上述三个操作对象。(大家可以把这里看作reduxaction的概念)。如下,是进行新增操作的对象

{
    type: ReactMultiChildUpdateTypes.INSERT_MARKUP,
    content: markup,
    fromIndex: null,
    fromNode: null,
    toIndex: toIndex,
    afterNode: afterNode,
}

在遍历的过程中,react不会一发现需要更新的节点就立即更新到真实dom上,而是将所有的上述差异对象,全部放入差异队列中,然后通过patch再将其更新到真实的dom上。

patch

patch是指遍历差异队列依次更新到真实dom上的操作。通过switch去匹配差异对象的type,然后进行对应的操作。
=>patch源码在这里

参考文档

  1. react源码--renderers/shared/reconciler/ReactMultiChild.js
  2. React源码之Diff算法
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容