react源码

https://xiaochen1024.com/article_item/600ac4384bf83f002edaf54a

以下用到了什么数据结构或方式
  • scheduler:小顶堆
  • 调度:messageChannel通信
  • render阶段的reconciler中:fiber、update、链表
  • diff算法:dfs(深度优先遍历)
  • lane模型:二进制掩码(”用一串二进制数字(掩码)去操作另一串二进制数字“的意思。)
react分为几个模块
  • scheduler(调度器):排列优先级,优先级高的先执行reconciler
  • reconciler(协调器):render阶段(主要工作:构建Fiber树和生成effectList),找哪个节点改变,打不同的tag(形成effectlist链表,记录需要更新的节点),创建或更新fiber节点(diff算法),采用深度优先遍历
  • renderer(渲染器):commit阶段,将reconciler打好标签的节点(主要遍历effectList),渲染到视图

scheduler、reconciler在内存中进行,不影响真实节点
react 17版本的出现,带来了全新的concurrent mode,包含一类功能的合集(fiber、scheduler、lane、suspense),核心是实现了一套,异步可中断,带优先级的更新

其他
  • $$typeof表示的是组件的类型
  • jsx对象上没有优先级、状态、effectTag等标记,fiber对象上有
scheduler时间片

js执行线程和GUI也就是浏览器的绘制是互斥的,如果在时间内,没有执行完js,则暂停执行,将执行权交还给浏览器绘制,等下一帧继续执行

  • 任务暂停:shouldYield(当前时间 > 任务开始的时间+yieldInterval,打断任务进行)就是用来判断剩余的时间有没有用尽,用尽了则让权

  • 调度优先级:两个函数创建具有优先级的任务
    1、runWithPriority: 以一个优先级执行callback,若为同步任务,则优先级为ImmediateSchedulerPriority
    2、scheduleCallback:以一个优先级注册callback,适当时机执行,涉及过期时间运算,所以粒度更细
    1)优先级意味着过期时间(时间点)。过期时间 = 开始时间(当前时间) + timeout,过期时间 < 当前时间,则要立即执行,过期时间越长,执行优先级越低
    2)scheduleCallback调度过程使用了【小顶堆】,所以每次都能取到离过期时间最近的任务
    3)未过期任务task存放在timerQueue中,过期任务(每次先执行这个)存放在taskQueue中。

  • 暂停后恢复执行: 在performConcurrentWorkOnRoot函数的结尾有这样一个判断,如果callbackNode等于originalCallbackNode那就恢复任务的执行

image.png
Lane

每个优先级是一个31位的二进制数字,1表示位置可用,0表示位置不可用(转换为10进制,数值越小,优先级越高),Lane的优先级粒度更细,ps:二进制计算性能更高

  • task任务怎么获取优先级的:从高优先级的lanes往下找,没有则换到稍微低一点优先级的lans里继续找
  • 高优先级怎么插队:低优先级已经构建了一部分fiber树,将其还原
  • 怎么解决饥饿问题:(低优先级的任务也要被执行),优先级调度过程中,遍历【未执行的任务包含的lane】,计算过期时间,加入root.expiredLanes,下次调用时优先返回expiredLanes(到期lane)
fiber双缓存
  • fiber是在内存中的dom,包含节点的属性、类型、dom。
  • 通过child、sibling、return(返回父节点)构成fiber树
  • 还保存了updateQueue(链表结构),用来计算state,updateQueue有多个未计算的update,update(一种数据结构)保存了更新的数据、优先级(过期时间)
  • fiberRoot:指整个应用的根节点,只存在一个
  • rootFiber:应用的节点,可以存在多个
  • 当前的fiber树和更新的fiber树切换的时候:fiberRoot的current指向更新的fiber树(即指向rootFiber)
render阶段
  • 捕获阶段:beginWork,从应用的根结点rootfiber开始到叶子结点,主要工作是创建或复用子fiber节点
  • 冒泡阶段:completeWork,主要工作是处理fiber的props、创建dom(创建的dom节点赋值给fiber.stateNode)、创建effectList
  • render阶段,当遍历到只有一个子节点的Fiber时,该Fiber节点的子节点不会执行beginWork和completeWork,这是react的一种优化手段
  • 在render阶段的末尾会调用commitRoot(fiberRoot),进入commit阶段
commit阶段
  • 遍历render阶段生成的effectList(fiber节点保存着props变化)
  • 遍历effectList对应的dom操作、生命周期、hook回调、销毁函数
image.png
diff算法(单节点diff、双节点diff)

diff算法三个前提

  • 同级dom比较
  • type不同,则销毁当前节点和子孙节点,并新建节点
  • 同一层级的节点,使用唯一key值来区分

单节点diff(Element、Portal、string、number)

  • key、type都相同,复用
  • key不同,删除节点并创建新的
  • key同、type不同,删除当前节点、以及与兄弟节点的标记,创建新节点

多节点diff(Array、Iterator)
会经历三次遍历(而newChildren存在于jsx当中)

  • 第一次:处理节点更新(props更新,type更新、删除)
    1、key不同,结束第一次遍历
    2、newChildren或oldFiber遍历完,结束第一次遍历(newChildren遍历完但oldFiber存在,则剩余的全都打deletion标签)
    3、key同,type不同,打deletion标签
    4、key、type都同,复用
  • 第二次:处理节点新增
    1、newChildren和oldFiber都遍历完,多节点diff结束
    2、newChildren、oldFiber都没遍历完,进入节点移动逻辑
    3、newChildren没遍历完,oldFiber遍历完,newChildren剩余值打【插入】的tag
  • 第三次:处理节点位置改变
    1、对比newChildren和oldFiber的各个节点,newChildren[i]能和oldFiber[j]位置对比的上,则i++,j++;否则oldFiber值移动到最后,j++,在与i进行比较

你可能感兴趣的:(react源码)