React的diff算法详解

一、什么是diff算法?

为了增强用户体验,React从版本16开始将同步更新重构成了可中断的异步更新,即采用了新的Reconciler(协调器,用于找出变化的组件),而新的Reconciler中采用了fiber架构。fiber架构的原理在此不再详细解释,我们目前只需要知道fiber节点可以保存dom信息,fiber节点构成的树叫fiber树,而更新dom是要用到‘双缓存技术’,即比较旧的fiber树与此次要渲染的jsx对象,返回新的fiber树进行渲染。在旧fiber树与jsx对象比较时,决定哪些节点要复用的过程,就是diff算法

由于diff本身也会带来性能消耗,为了降低算法复杂度,React对diff做了三个预设限制

  1. 只对同级元素进行diff,如果某元素在更新之后跨越了层级,那么React不会复用它
  2. 两个不同类型的元素会产生两颗不同的树,即如果元素由div变成p,那么React会删除div及其子孙节点,新建p及其子孙节点
  3. 开发者可以使用key参数表示哪些元素在不同的渲染下保持稳定,例如
    更新前
<p key='1'></p>
<div key='2'></div>

更新后

<div key='2'></div>
<p key='1'></p>

如果没有key会走第二条限制,有了key,react就可以判断div和p节点是存在的,可以复用,只需要交换顺序。

diff算法会根据不同的jsx对象执行不同的处理函数,根据jsx对象的不同,我们可以分为两类

1.JSX对象(之后都用newChildren表示)的类型为object、number、string,代表同级只有一个节点
2. newChildren的类型为Array,代表同级有多个节点。

二、单节点diff

对于单节点diff,用一个流程图就可以解释

image.png

注意:比较newChildren与current fiber(之后称为oldFiber)时,只有当key相同且元素类型相同时,dom节点才可以复用,如果fiber上的dom信息存在,而且key相同但元素类型不同,那么要删除该fiber及其所有兄弟节点,如果dom信息存在,但key不同,那么只是删除该fiber节点。例如
更新前

<div>1</div>
<div>2</div>
<div>3</div>

更新后

<p>1</p>

由于key的默认值为null,所以更新前与更新后满足key相同且元素类型不同,那么我们要删除更新前的三个div节点,新增p节点

三、多节点diff

对于多节点diff, 我们要遍历newChildren和oldFiber进行比较。由于React团队发现dom节点一般有更新,增加,删除这三种操作,而更新更为频繁,所以他们设置更新的优先级高于增加删除。基于以上原因,在多节点diff算法的实现中有两层遍历,第一层遍历处理更新的节点,第二层遍历处理更新以外的节点

第一层遍历

遍历newChildren与oldFiber, 判断节点是否可复用,如果可以复用,则继续遍历。
如果不能复用,分为两种情况:

  1. key相同但type不同,那么将oldFiber标记为DELETION,继续遍历
  2. key不相同,立即跳出遍历。

第二层遍历

第二层遍历从第一层遍历的结束位开始
第一层遍历结束后有4种结果

  1. newChildren与oldFiber都遍历完,此时我们只需要在第一遍遍历的基础上进行更新。
  2. newChildren遍历完,oldFiber没有遍历完说明有节点被删除了,那我们只需要遍历剩下的oldFiber,并打上DELETION标记。
  3. newChildren没有遍历完,oldFiber遍历完说明增加了一些节点,那我们需要遍历剩下的newChildren为生成的workInProgress Fiber打上Placement标签
  4. newChildren和oldFiber都没有遍历完 ,这主要是由于遍历到的节点key不相同导致的。这说明有节点的位置改变了。通过比较newChildren中的节点与其在oldFiber中的位置信息,我们可以知道它的相对顺序。

首先我们要判断newChildren中遍历到的节点,在oldFiber中是否存在,基于此,React将oldFiber中的节点以key-oldfiber 键值对的形式存在Map中,只需要newChildren的key,就可以判断oldFiber中有没有相应的节点。

如果oldFiber中没有相应的节点,则将newChildren生成的fiber打上placement标记

如果有相应的节点,将它的索引记为oldIndex,与上一次可复用节点在oldFiber的索引位置lastPlacedIndex比较,如果每次可复用的节点在上一次可复用右边就说明位置没有变化,即

oldIndex >=lastPlacedIndex, 说明相对位置没有变化,那么令lastPlacedIndex=oldIndex
oldIndex<lastPlacedIndex, 代表本节点需要向右移动
例如:

//更新后
abcd
//更新前
acbd

第一次遍历, a节点可复用,lastPlacedIndex=0, b节点key不同,跳出遍历
第二次遍历
//更新后
bcd
//更新前
cbd
1.遍历b,发现它的oldIndex=2 ( 在acbd中的位置),由于oldIndex>lastPlacedIndex ,则lastPlacedIndex=oldIndex
2. 遍历c,oldIndex=1,则oldIndex<lastPlacedIndex,  则本次更新节点需要向右移动,阅读源码发现是打了Placement标签
3. 遍历d, oldIndex=3, oldIndex>lastPlacedIndex, 那么lastPlacedIndex=oldIndex, 遍历结束

参考文档
React技术揭秘 (iamkasong.com)

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

推荐阅读更多精彩内容