您的位置:首页 > 新闻 > 热点要闻 > 兰州h5页面制作_网址生成_学seo的培训学校_百度经验怎么赚钱

兰州h5页面制作_网址生成_学seo的培训学校_百度经验怎么赚钱

2025/2/24 14:05:25 来源:https://blog.csdn.net/qq_34306228/article/details/145612134  浏览:    关键词:兰州h5页面制作_网址生成_学seo的培训学校_百度经验怎么赚钱
兰州h5页面制作_网址生成_学seo的培训学校_百度经验怎么赚钱

React核心源码解析

  • diff
    • 单一节点比较diff
    • 多节点比较diff
      • 两轮遍历比较
        • 第一轮比较
        • 第二轮比较
  • Update 状态更新
  • Concurrent Mode

diff

一共两个阶段

  • render:内存中的更新,主要是通过递归的过程,来将react变化的部分,在内存中找到哪些元素是需要发生变化的,针对需要发生变化的内容,会打上标,也就是Update,针对这些需要Update的组件,会拿着上一次与当前这一次的节点,通过Fiber(专门的数据结构存储当前的组件/模块)进行此次与上一次元素更新之间对比。
    diff 的过程,其实就是Update Fiber的过程,diff的结果就是生成一个经过Update更新之后的新的Fiber的节点。
  • commit:同步的过程,同步渲染到浏览器端/客户端的过程
  1. 针对不同类型的元素

<div><Component />	
</div>

<span><Component />	
</span>

这是不同类型元素的展示,在Fiber中,定义当前组件类型不一致的时候,是需要将当前树全部销毁重建的
因此,在开发过程中,需要尽可能减少元素的变化。

  1. 针对相同类型元素

<ul className="hello"><li>1</li>	<li>2</li>	
</ul>

<ul className="world"><li>3</li>	<li>4</li><li>5</li>		
</ul>

就是在render的阶段中,相同类型的节点进行diff判断,并不会立马更新。生成mutation要变化的内容,针对不同diff进行汇总,然后在commit过程中,对dom的节点直接进行操作。

但是 上述例子中,如果是针对第一个元素<li>之前进行添加的话,会将所有的子列表全部销毁掉,重新创建,
就比如,从

<ul className="hello"><li>1</li>	<li>2</li>	
</ul>

<ul className="world"><li>3</li>	<li>1</li><li>2</li>		
</ul>

这样的情况
这时候,通过key的方式去解决,并且尽可能保证每个key是唯一的,不要去使用index作为key来传输

跟一个真实DOM相关联的是:

  • current fiber:当前的fiber节点,表达当前DOM的 内存对象
  • workInProgress fiber:内存中变化的节点,表达接下来在内存中需要进行操作的对象
  • DOM:在commit过程中生成的真实的dom
  • JSX:真实代码

通过current fiberJSX比较,更新到workInProgress fiber,进行current fiber和workInProgress fiber的互换,拿着workInProgress fiber替换生成到真实DOM

  1. 同级元素的比较
  2. 不同类型元素 销毁当前节点&所有子节点
  3. key
// 根据newChild类型选择不同diff函数处理
function reconcileChildFibers(returnFiber: Fiber,currentFirstChild: Fiber | null,newChild: any,
): Fiber | null {//这里的newChild是fiber节点,不是dom元素了const isObject = typeof newChild === 'object' && newChild !== null;//如果是对象的话,那么就是单一的节点if (isObject) {// object类型,可能是 REACT_ELEMENT_TYPE 或 REACT_PORTAL_TYPEswitch (newChild.$$typeof) {case REACT_ELEMENT_TYPE:// 调用 reconcileSingleElement 处理// // ...省略其他case}}if (typeof newChild === 'string' || typeof newChild === 'number') {// 调用 reconcileSingleTextNode 处理// ...省略}//如果是数组的话,则是多个节点if (isArray(newChild)) {// 调用 reconcileChildrenArray 处理// ...省略}// 一些其他情况调用处理函数// ...省略// 以上都没有命中,删除节点return deleteRemainingChildren(returnFiber, currentFirstChild);
}

单一节点比较diff

function reconcileSingleElement(returnFiber: Fiber,currentFirstChild: Fiber | null,element: ReactElement
): Fiber {const key = element.key;let child = currentFirstChild;// 首先判断是否存在对应DOM节点while (child !== null) {// 上一次更新存在DOM节点,接下来判断是否可复用// 首先比较key是否相同if (child.key === key) {// key相同,接下来比较type是否相同switch (child.tag) {// ...省略casedefault: {if (child.elementType === element.type) {// type相同则表示可以复用// 返回复用的fiberreturn existing;}// type不同则跳出switchbreak;}}// 代码执行到这里代表:key相同但是type不同// 将该fiber及其兄弟fiber标记为删除deleteRemainingChildren(returnFiber, child);break;} else {// key不同,将该fiber标记为删除deleteChild(returnFiber, child);}child = child.sibling;}// 创建新Fiber,并返回 ...省略
}

针对单一的节点:

  • key是否一样
    • 一样
      • type 一样:同一个DOM
      • type不一样:当前节点和所有的兄弟节点sibling全都删除
    • 不一样
      • 直接删除

举例:
全部删除:

// 更新前
<div>a</div>
// 更新后
<p>a</p>// 更新前
<div key="xxx">a</div>
// 更新后
<div key="ooo">a</div>

只更新内容:

// 更新前
<div key="xxx">a</div>
// 更新后
<div key="xxx">b</div>

多节点比较diff

isArray方法比较

  1. 节点更新
// 更新前
<ul><li key="0" className="before">0<li><li key="1">1<li>
</ul>// 更新后 情况1 —— 节点属性变化
<ul><li key="0" className="after">0<li><li key="1">1<li>
</ul>// 更新后 情况2 —— 节点类型更新
<ul><div key="0">0</div><li key="1">1<li>
</ul>

节点属性变化,只需要更新即可
节点类型变化,则当前<li>和兄弟节点全部推倒重建

  1. 节点新增和删除
// 更新前
<ul><li key="0">0<li><li key="1">1<li>
</ul>// 更新后 情况1 —— 新增节点
<ul><li key="0">0<li><li key="1">1<li><li key="2">2<li>
</ul>// 更新后 情况2 —— 删除节点
<ul><li key="1">1<li>
</ul>
  1. 节点位置变化,顺序调整
// 更新前
<ul><li key="0">0<li><li key="1">1<li>
</ul>// 更新后
<ul><li key="1">1<li><li key="0">0<li>
</ul>

这里key不一样,全部删除重建

两轮遍历比较

第一轮比较
  1. let i = 0,遍历newChildren,将newChildren[i]与oldFiber比较,判断DOM节点是否可复用;
    JSX:newChildren[i]
    currentFiber:oldFiber
  2. 如果可复用,i++,继续比较newChildren[i]与oldFiber.sibling,可以复用则继续遍历;
  3. 如果不可复用,分两种情况:
    a. key不同导致不可复用,立即跳出整个遍历,第一轮遍历结束;
    b. key相同type不同导致不可复用,会将oldFiber标记mutation为DELETION,并继续遍历;
  4. 如果newChildren遍历完(即 i === newChildren.length - 1 )或者oldFiber遍历完(即oldFiber.sibling === null),跳出遍历,第一轮遍历结束;
// 更新前
<li key="0">0</li>
<li key="1">1</li>
<li key="2">2</li>// 更新后
<li key="0">0</li>
<li key="2">1</li>
<li key="1">2</li>// 第一个节点可复用,遍历到key === 2的节点发现key改变,不可复用
// 跳出遍历,等待第二轮遍历处理// oldFiber: key === 1、key === 2未遍历
// newChildren剩下key === 2、key === 1未遍历
// 更新前
<li key="0" className="a">0</li>
<li key="1" className="b">1</li>// 更新后 情况1 —— newChildren与oldFiber都遍历完
<li key="0" className="aa">0</li>
<li key="1" className="bb">1</li>// 更新后 情况2 —— newChildren没遍历完,oldFiber遍历完
// newChildren剩下 key==="2" 未遍历
<li key="0" className="aa">0</li>
<li key="1" className="bb">1</li>
<li key="2" className="cc">2</li>// 更新后 情况3 —— newChildren遍历完,oldFiber没遍历完
// oldFiber剩下 key==="1" 未遍历
<li key="0" className="aa">0</li>
第二轮比较
  • newChildren和oldFiber都遍历完了,针对单一节点标记mutation,需要发生变化的元素记录在render阶段中,加update更新

  • newChildren剩下了,oldFiber没剩下,意味着新增了
    剩余的reset newChildren直接添加到workInProgress Fiber
    新增元素打标,将mutation记为 Placement

  • newChildren 没剩下 oldFiber 剩下了,意味着删除了
    workInProgress Fiber中将旧的循环遍历掉,标记mutationDELETION

  • newChildren和oldFiber都剩下了
    第二轮遍历:
    const exisitingChildren = map(),通过map.get获取oldFiber剩下的所有存在existingChildren中

    • key:oldFiber中的key
    • value:oldFiber中的value

    遍历 newChildren 剩下的
    exisitingChildren.has(newChildren[i].key),有的话获取这个元素,然后删除掉
    lastPlacedIndex:最后一个可复用的节点,再当前oldFiber的索引位置

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Update 状态更新

触发状态更新的方式

  • 初始化的render
  • setState
  • useState
  • forceUpdate

进行状态更新时候会创建一个Update 对象存储变更相关联的内容
包含到对应的fiber,在beginWork的过程中,找到这个fiber,记录到当前更新的数组队列updateQueue

Update是在render beginWork找到要更新的元素,Update置入要更新的节点中,去触发它的state

// 一次更新的内容
const update: Update<*> = {eventTime, //获取当前的执行时间,判断执行更新的耗时lane, //优先级任务suspenseConfig, tag: UpdateState, //包裹住更新的状态,updateState | ReplaceState | forceUpdatepayload: null, //不同的更新元素带有的参数是什么callback: null,next: null, // 也是一个链表,下一次更新的内容
};
//一次更新的 update1 next->update2 next->update3 
//div1 div2

fiber 节点 updateQueue指代的是div1,div2的变化

//一个更新的fiber的节点
const queue: UpdateQueue<State> = {baseState: fiber.memoizedState, //fiber中本身的statefirstBaseUpdate: null, //第一个更新的lastBaseUpdate: null, //最后一个更新的shared: {pending: null, //关联链表的方式,第一个更新,第二个更新,...最后一个更新,通过这个来关联的},effects: null,};

在这里插入图片描述
在这里插入图片描述
pending候车的上车
先上u3
在这里插入图片描述

在这里插入图片描述
这都是在render中做的,进入到commit阶段后,不管是谁,都不能被中断了,因为都已经在视图中了
因此,说的 异步可中断,说的都是在内存中能够做到的事情

u2优先级高于u1
但是链表:u1 — u2 这个顺序始终不会变
也就意味着,由于优先级,先执行的是u2,然后再执行一次 u1 和 u2。由此,高优先级的任务可能会触发两次

Concurrent Mode

协调模式

  • Fiber 异步可中断
  • Scheduler 协调器,结合可分片,异步可中断
  • lane 优先级
  1. 优先级不同
  2. 优先级表达的方式 batch批次执行
  3. 方便计算

划分二进制31位内容

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com