React16源码: React中commit阶段的commitAllLifeCycles的源码实现

commitAllLifeCycles


1 )概述

  • 在 react commit 阶段的 commitRoot 第三个 while 循环中
  • 处理了生命周期相关的一些内容
  • 它这个方法的名字叫做 commitAllLifeCycles

2 )源码

定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L479

查看 commitAllLifeCycles

function commitAllLifeCycles(finishedRoot: FiberRoot,committedExpirationTime: ExpirationTime,
) {if (__DEV__) {ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();ReactStrictModeWarnings.flushLegacyContextWarning();if (warnAboutDeprecatedLifecycles) {ReactStrictModeWarnings.flushPendingDeprecationWarnings();}}// 通过遍历 firstEffect 到 lastEffect 的单项链表,基于每一个effect对应的fiber对象,对它做出对应的操作while (nextEffect !== null) {const effectTag = nextEffect.effectTag;// 这边的操作设置的是 update 和 callback ,如果有 Update | Callback 就执行 commitLifeCyclesif (effectTag & (Update | Callback)) {recordEffect();const current = nextEffect.alternate;commitLifeCycles(finishedRoot,current,nextEffect,committedExpirationTime,);}if (effectTag & Ref) {recordEffect();commitAttachRef(nextEffect);}if (enableHooks && effectTag & Passive) {rootWithPendingPassiveEffects = finishedRoot;}nextEffect = nextEffect.nextEffect;}
}
  • 进入 commitLifeCycles

    function commitLifeCycles(finishedRoot: FiberRoot,current: Fiber | null,finishedWork: Fiber,committedExpirationTime: ExpirationTime,
    ): void {// 根据不同的组件类型来执行不同的操作switch (finishedWork.tag) {case FunctionComponent:case ForwardRef:case SimpleMemoComponent: {commitHookEffectList(UnmountLayout, MountLayout, finishedWork);break;}// 主要是 ClassComponent,它会根据 current 是否等于 null 来执行不同的生命周期方法// 一个是 componentDidMount 跟 componentDidUpdate// 因为我们只有在第一次渲染的时候才会调用 componentDidMount 这个生命周期方法// 后续调用的都是 componentDidUpdate 它们的调用方式会有一定的区别// 这里是 ClassComponent 它的一个commit的过程case ClassComponent: {const instance = finishedWork.stateNode;if (finishedWork.effectTag & Update) {// 第一次调用if (current === null) {startPhaseTimer(finishedWork, 'componentDidMount');// We could update instance props and state here,// but instead we rely on them being set during last render.// TODO: revisit this when we implement resuming.if (__DEV__) {if (finishedWork.type === finishedWork.elementType) {warning(instance.props === finishedWork.memoizedProps,'Expected instance props to match memoized props before ' +'componentDidMount. This is likely due to a bug in React. ' +'Please file an issue.',);warning(instance.state === finishedWork.memoizedState,'Expected instance state to match memoized state before ' +'componentDidMount. This is likely due to a bug in React. ' +'Please file an issue.',);}}instance.componentDidMount();stopPhaseTimer();} else {// 更新const prevProps =finishedWork.elementType === finishedWork.type? current.memoizedProps: resolveDefaultProps(finishedWork.type, current.memoizedProps);const prevState = current.memoizedState;startPhaseTimer(finishedWork, 'componentDidUpdate');// We could update instance props and state here,// but instead we rely on them being set during last render.// TODO: revisit this when we implement resuming.if (__DEV__) {if (finishedWork.type === finishedWork.elementType) {warning(instance.props === finishedWork.memoizedProps,'Expected instance props to match memoized props before ' +'componentDidUpdate. This is likely due to a bug in React. ' +'Please file an issue.',);warning(instance.state === finishedWork.memoizedState,'Expected instance state to match memoized state before ' +'componentDidUpdate. This is likely due to a bug in React. ' +'Please file an issue.',);}}instance.componentDidUpdate(prevProps,prevState,instance.__reactInternalSnapshotBeforeUpdate, // 在 commitRoot 的方法里面去生成的 instance 对象上面的 snapshot 快照);stopPhaseTimer();}}const updateQueue = finishedWork.updateQueue;if (updateQueue !== null) {if (__DEV__) {if (finishedWork.type === finishedWork.elementType) {warning(instance.props === finishedWork.memoizedProps,'Expected instance props to match memoized props before ' +'processing the update queue. This is likely due to a bug in React. ' +'Please file an issue.',);warning(instance.state === finishedWork.memoizedState,'Expected instance state to match memoized state before ' +'processing the update queue. This is likely due to a bug in React. ' +'Please file an issue.',);}}// We could update instance props and state here,// but instead we rely on them being set during last render.// TODO: revisit this when we implement resuming.// 对于 ClassComponent,还需要做一件事情,就是去获取它的 updatequeen// 然后 执行 commitUpdateQueue 这么一个方法,我们要拿到他的 propscommitUpdateQueue(finishedWork,updateQueue,instance,committedExpirationTime,);}return;}// 对于 HostRoot,它也会执行 commitUpdateQueue// 因为对于 HostRoot 来说,基本上只有在调用 ReactDOM.render 的时候才会创建update// 它的 commitUpdateQueue, 能做什么呢?对于调用 ReactDOM.render 是可以传3个参数的// 第3个参数就是在第一次 render 结束之后,它的一个回调, 执行过程类似case HostRoot: {const updateQueue = finishedWork.updateQueue;if (updateQueue !== null) {let instance = null;// 注意这里,有子树的场景if (finishedWork.child !== null) {switch (finishedWork.child.tag) {// 这里case HostComponent:instance = getPublicInstance(finishedWork.child.stateNode); // 获取 对应 HostComponent 的 dom 节点,就是返回传参break;// 这里,其实和上面一样case ClassComponent:instance = finishedWork.child.stateNode;break;}}commitUpdateQueue(finishedWork,updateQueue,instance,committedExpirationTime,);}return;}case HostComponent: {const instance: Instance = finishedWork.stateNode;// Renderers may schedule work to be done after host components are mounted// (eg DOM renderer may schedule auto-focus for inputs and form controls).// These effects should only be committed when components are first mounted,// aka when there is no current/alternate.// 参考下面的 commitMount 注释if (current === null && finishedWork.effectTag & Update) {const type = finishedWork.type;const props = finishedWork.memoizedProps;commitMount(instance, type, props, finishedWork);}return;}case HostText: {// We have no life-cycles associated with text.return;}case HostPortal: {// We have no life-cycles associated with portals.return;}case Profiler: {if (enableProfilerTimer) {const onRender = finishedWork.memoizedProps.onRender;if (enableSchedulerTracing) {onRender(finishedWork.memoizedProps.id,current === null ? 'mount' : 'update',finishedWork.actualDuration,finishedWork.treeBaseDuration,finishedWork.actualStartTime,getCommitTime(),finishedRoot.memoizedInteractions,);} else {onRender(finishedWork.memoizedProps.id,current === null ? 'mount' : 'update',finishedWork.actualDuration,finishedWork.treeBaseDuration,finishedWork.actualStartTime,getCommitTime(),);}}return;}case SuspenseComponent:break;case IncompleteClassComponent:break;default: {invariant(false,'This unit of work tag should not have side-effects. This error is ' +'likely caused by a bug in React. Please file an issue.',);}}
    }
    
    • 进入 commitUpdateQueue
      // packages/react-reconciler/src/ReactUpdateQueue.js#L571
      export function commitUpdateQueue<State>(finishedWork: Fiber,finishedQueue: UpdateQueue<State>,instance: any,renderExpirationTime: ExpirationTime,
      ): void {// If the finished render included captured updates, and there are still// lower priority updates left over, we need to keep the captured updates// in the queue so that they are rebased and not dropped once we process the// queue again at the lower priority.// 这里做了一个判断,是否有 firstCapturedUpdate,在 finishedqueen 上面, 也就是组件的 updateQueen 上面// 这个 updateQueue上面, 如果有 capturedupdate,就是说我们在渲染这个组件的子树的时候,如果有异常出现// 并且被这个组件捕获,会产生这部分的update, 这部分的update,有可能在这一次的渲染周期里面, 没有被执行完// 其实这部分它有一个用意,就是说对于被捕获的错误,我们如果在这一次渲染周期里面无法去处理// 没有办法去完成它,我们把它放到这个组件上面,如果它还有低优先级的更新的话// 那么我们把它放到低优先级的更新上面去看他是否在那个低优先级的更新上能够被处理// 如果我们没有低优先级的处理了, 如果我们没有低优先级的更新了// 我们在本次渲染当中捕获的更新,就直接给它清空了// 因为我们本次渲染里面没有完成,为了不影响之后产生的更新,我们就直接清空它了// 而不需要去让它在后续影响整个组件的一个更新过程if (finishedQueue.firstCapturedUpdate !== null) {// Join the captured update list to the end of the normal list.// 这个时候它会进行一个判断, 如果我们现在这个组件上面还有 update 不是的 capturedUpdate, 而是普通的我们自己创建的updateif (finishedQueue.lastUpdate !== null) {// 就把capturedupdate放到正常的update的后面,就把它在链表上面去给它接在最后面这一部分finishedQueue.lastUpdate.next = finishedQueue.firstCapturedUpdate;finishedQueue.lastUpdate = finishedQueue.lastCapturedUpdate;}// Clear the list of captured updates.// 不管有没有,我们都要执行,把firstCapturedUpdate到lastCapturedUpdate这个链表上面的数据清空finishedQueue.firstCapturedUpdate = finishedQueue.lastCapturedUpdate = null;}// Commit the effects// commitUpdateEffects 它不仅仅要对 firstEffect 来执行commitUpdateEffects(finishedQueue.firstEffect, instance);finishedQueue.firstEffect = finishedQueue.lastEffect = null;// 而同样的对于 capturedEffect 也要进行执行// 比如之前 workLoop 中看到过的 throw exception 里面// 去捕获了这个错误,然后为 classcomponent 创建了一个 update// throw exception 里面,在 unwindwork 中的 createClassErrorUpdate, 在里面 放入了update它的callback// 就是去调用 componentDidCatch 这个生命周期方法,这个callback 也是会在这个地方被执行的commitUpdateEffects(finishedQueue.firstCapturedEffect, instance);finishedQueue.firstCapturedEffect = finishedQueue.lastCapturedEffect = null;
      }
      
      • 进入 commitUpdateEffects
        function commitUpdateEffects<State>(effect: Update<State> | null,instance: any,
        ): void {while (effect !== null) {const callback = effect.callback;// 判断一下这个 effect 上面是否有 callbackif (callback !== null) {effect.callback = null;// 如果有callback,我们执行callbackcallCallback(callback, instance);}effect = effect.nextEffect;}
        }
        // setState 中的 回调就是在这个时候执行的
        // 在这个时间点上,这个 update 对应的更新已经执行了,它已经反映到了我们的 state 和 props 上面
        // 在 setState 中的 回调才会执行
        // 在之前的 processUpdateQueue 方法里面,没有执行回调的过程的,如果有回调,回调就会把它放到对应的 update 里面
        function callCallback(callback, context) {invariant(typeof callback === 'function','Invalid argument passed as callback. Expected a function. Instead ' +'received: %s',callback,);callback.call(context);
        }
        
    • 进入 commitMount
      // 先判断了一下是否要 autofocus 这个 HostComponent
      // 在之前 completeUnitOfWork 的时候,会判断过这个组件是否有 autofocus 这个属性的
      // 然后会给它加上一个 update 的 SideEffect
      // 而在这里,同样判断它是是否有 update 这个 SideEffect
      // 同时它只有在 current 等于 null 的一个情况下才会被调用 (限制条件在外部调用方)
      // 其实 这也就符合我们对于 autofocus 的一个认知
      // 在页面初次渲染的时候,它才会去自动获取需要被 autofocus 的节点 
      // 同样的对于react当中它的一个判断方式就非常的简单,就是通过current是否等于null来进行判断。
      // 如果有update,我们就执行这个方法来判断一下它是否是一个autofocus的组件。
      // 然后进行一个手动触发这个focus的过程
      export function commitMount(domElement: Instance,type: string,newProps: Props,internalInstanceHandle: Object,
      ): void {// Despite the naming that might imply otherwise, this method only// fires if there is an `Update` effect scheduled during mounting.// This happens if `finalizeInitialChildren` returns `true` (which it// does to implement the `autoFocus` attribute on the client). But// there are also other cases when this might happen (such as patching// up text content during hydration mismatch). So we'll check this again.if (shouldAutoFocusHostComponent(type, newProps)) {((domElement: any):| HTMLButtonElement| HTMLInputElement| HTMLSelectElement| HTMLTextAreaElement).focus();}
      }function shouldAutoFocusHostComponent(type: string, props: Props): boolean {switch (type) {case 'button':case 'input':case 'select':case 'textarea':return !!props.autoFocus;}return false;
      }
      
  • 以上是对所有的具有 SideEffect 的节点在commit过程当中执行任务的一个过程,到这里,整个dom树其实也已经渲染上去了

  • commitAllLifeCycles 其实主要是调用不同组件的一个生命周期,以及对于 HostComponent,等组件的不同处理

  • 包括可能存在 autofocus 的处理过程,所以它相当于是在整个应用渲染完成之后的一个善后工作

  • 具体细节,写在上述代码的注释中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/652769.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

应用机器学习的建议

一、决定下一步做什么 在你得到你的学习参数以后&#xff0c;如果你要将你的假设函数放到一组新的房屋样本上进行测试&#xff0c;假如说你在预测房价时产生了巨大的误差&#xff0c;你想改进这个算法&#xff0c;接下来应该怎么办&#xff1f;实际上你可以考虑先采用下面的几种…

防御保护--第一次实验

目录 一&#xff0c;vlan的划分及在防火墙上创建单臂路由 二&#xff0c;创建安全区域 三&#xff0c;配置安全策略 四&#xff0c;配置认证策略 五&#xff0c;配置NAT策略 1.将内网中各个接口能够ping通自己的网关 2..生产区在工作时间内可以访问服务器区&#xff0c;仅…

AI大模型开发架构设计(6)——AIGC时代,如何求职、转型与选择?

文章目录 AIGC时代&#xff0c;如何求职、转型与选择&#xff1f;1 新职场&#xff0c;普通人最值钱的能力是什么?2 新职场成长的3点建议第1点&#xff1a;目标感第2点&#xff1a;执行力第3点&#xff1a;高效生产力 3 新职场会产生哪些新岗位机会?如何借势?4 新职场普通人…

数据结构-线性表

文章目录 数据结构—线性表1.线性表的定义和基本操作线性表的定义线性表的特点线性表的基本操作 2.线性表的顺序存储和链式存储表示顺序存储链式存储单链表循环链表双向链表 数据结构—线性表 1.线性表的定义和基本操作 线性表的定义 定义&#xff1a;线性表是具有相同数据类…

数据结构篇-03:堆实现优先级队列

本文着重在于讲解用 “堆实现优先级队列” 以及优先级队列的应用&#xff0c;在本文所举的例子中&#xff0c;可能使用优先级队列来解并不是最优解法&#xff0c;但是正如我所说的&#xff1a;本文着重在于讲解“堆实现优先级队列” 堆实现优先级队列 堆的主要应用有两个&…

sqli-lbs靶场搭建

目录 环境小皮源码下载 1.源码解压&#xff1a; 2.搭建网站 2.1点击创建网站 2.2修改sql-connections\db-creds.inc 2.3重新启动 3.访问你设置的域名 3.1点击启动数据库配置 3.2返回第一个页面&#xff08;开启题目&#xff09; sqlilbs靶场搭建 环境小皮源码下载 下载地址&am…

【服务器】宝塔面板的使用手册

目录 &#x1f337;概述 &#x1f33c;1. 绑定域名 &#x1f33c;2. 添加端口 &#x1f33c;3. 安装docker配置docker​​​​​​​ &#x1f33c;4. 软件商店 &#x1f33c;5. 首页 &#x1f337;概述 宝塔面板的安装教程&#xff1a;【服务器】安装宝塔面板 &#x1f…

golang封装业务err(结合iris)

golang封装业务err 我们有时在web开发时&#xff0c;仅凭httpStatus以及msg是不方便维护和体现我们的业务逻辑的。所以就需要封装我们自己的业务错误。 自定义biz_err维护err map&#xff1a;errorResponseMap、errorHttpStatusMap 注意&#xff1a;本文主要以演示为主&#xf…

uniapp 用css animation做的鲤鱼跃龙门小游戏

第一次做这种小游戏&#xff0c;刚开始任务下来我心里是没底的&#xff0c;因为我就一个‘拍黄片’的&#xff0c;我那会玩前端的动画啊&#xff0c;后面尝试写了半天&#xff0c;当即我就给我领导说&#xff0c;你把我工资加上去&#xff0c;我一个星期给你做出来&#xff0c;…

php获取网卡的MAC地址原码;目前支持WIN/LINUX系统

<?php /** 获取网卡的MAC地址原码&#xff1b;目前支持WIN/LINUX系统 获取机器网卡的物理&#xff08;MAC&#xff09;地址 **/ class GetMacAddr{ var $return_array array(); // 返回带有MAC地址的字串数组 var $mac_add…

Vulnhub靶场DC-9

攻击机192.168.223.128 靶机192.168.223.138 主机发现 nmap -sP 192.168.223.0/24 端口扫描 nmap -sV -p- -A 192.168.223.138 开启了22 80端口 访问一下web页面 有个查询界面 测试发现存在post型的sql注入 用sqlmap跑一下&#xff0c;因为是post型的&#xff0c;这里…

用于不对称卷积的验证参数的小程序

非对称卷积的特征图尺寸计算 此处只例举输入图像是正方形的情况。设输入图像尺寸为WxW&#xff0c;卷积核尺寸为ExF&#xff0c;步幅为S&#xff0c;Padding为P&#xff0c;卷积后的特征图尺寸为&#xff1a; 矩形卷积 如果输入图像是正方形&#xff0c;尺寸为WxW&#xff0c…

C++二叉搜索树详解

文章目录 1. 前言2. 二叉搜索树的概念3. 二叉搜索树的操作4. 二叉搜索树的实现5. 二叉搜索树的应用6. 二叉搜索树的性能分析 1. 前言 当涉及到组织和管理数据时&#xff0c;二叉搜索树是一种常用的数据结构。它不仅可以快速插入和删除元素&#xff0c;还可以高效地搜索和查找特…

双系统Linux使用parted缩短NTFS分区并新建EXT4分区

调整分区 安装 parted&#xff08;如果未安装&#xff09;&#xff1a; sudo apt-get install parted # 对于基于 Debian/Ubuntu 的系统 sudo yum install parted # 对于基于 CentOS/RHEL 的系统运行 parted&#xff1a; sudo parted /dev/sdX这里的 /dev/sdX 是…

Elasticsearch安装Head图形插件

一、Google浏览器扩展插件方式 1.安装插件 进入谷歌浏览器应用商店搜索“Elasticsearch Head”,点击链接跳转 点击“添加至Chrome”按钮安装即可。 2.使用插件 在浏览器的插件列表多了个一个放大镜图标 点击“New”新建链接,输入es节点或集群地址。 连接成功 可以进行概括…

windows CUDA更新(最简单方法)+虚拟环境torch和cuda安装

目录 一、Torch和对应cuda安装 1、查看本身电脑的cuda版本 2、查找对应cuda——torch——python版本 3、安装cuda 4、安装torch 二、window 10 NVIDIA cuda版本更新 一、Torch和对应cuda安装 项目使用torch想要使用GPU运行&#xff0c;但是报错&#xff0c;记录一下解决…

前端安全之XSS、CSRF

XSS(cross-site-script)跨站脚本攻击 跨站脚本攻击是通过在网站中注入恶意代码&#xff0c;来达到劫取用户cookie信息&#xff0c;或者实施其他破坏行动。 例如&#xff1a;一个网站如果没有针对XSS做响应的安全措施&#xff0c;而且它存在添加评论的功能&#xff0c;那么用户…

扭蛋机小程序开发:探索用户体验与商业价值的融合

一、引言 随着移动互联网的快速发展&#xff0c;小程序作为一种新型的应用形态&#xff0c;正逐渐改变着人们的生活方式。扭蛋机小程序便是其中一例&#xff0c;它结合了线上线下的互动体验&#xff0c;为用户带来了全新的娱乐方式。本文将探讨扭蛋机小程序的开发过程&#xf…

初见CodeQL

安装CodeQL CodeQL本身包含两部分解析引擎SDK 下载已经编译好的 CodeQL 执行程序 https://github.com/github/codeql-cli-binaries/releases 下载之后配置环境变量 安装 SDK CMD 进入 CodeQL 安装目录&#xff0c;使用 Git 安装 SDK git clone https://github.com/Semmle/ql安…

Vulnhub-dc6

信息收集 # nmap -sn 192.168.1.0/24 -oN live.port Starting Nmap 7.94 ( https://nmap.org ) at 2024-01-25 14:39 CST Nmap scan report for 192.168.1.1 Host is up (0.00075s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nmap scan report for 192.168.1.2…