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

本文主要是介绍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 的处理过程,所以它相当于是在整个应用渲染完成之后的一个善后工作

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

这篇关于React16源码: React中commit阶段的commitAllLifeCycles的源码实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/651330

相关文章

vue基于ElementUI动态设置表格高度的3种方法

《vue基于ElementUI动态设置表格高度的3种方法》ElementUI+vue动态设置表格高度的几种方法,抛砖引玉,还有其它方法动态设置表格高度,大家可以开动脑筋... 方法一、css + js的形式这个方法需要在表格外层设置一个div,原理是将表格的高度设置成外层div的高度,所以外层的div需要

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

Java中List转Map的几种具体实现方式和特点

《Java中List转Map的几种具体实现方式和特点》:本文主要介绍几种常用的List转Map的方式,包括使用for循环遍历、Java8StreamAPI、ApacheCommonsCollect... 目录前言1、使用for循环遍历:2、Java8 Stream API:3、Apache Commons

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

MySQL分表自动化创建的实现方案

《MySQL分表自动化创建的实现方案》在数据库应用场景中,随着数据量的不断增长,单表存储数据可能会面临性能瓶颈,例如查询、插入、更新等操作的效率会逐渐降低,分表是一种有效的优化策略,它将数据分散存储在... 目录一、项目目的二、实现过程(一)mysql 事件调度器结合存储过程方式1. 开启事件调度器2. 创

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

基于Go语言实现一个压测工具

《基于Go语言实现一个压测工具》这篇文章主要为大家详细介绍了基于Go语言实现一个简单的压测工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理客户端模块Http客户端处理Grpc客户端处理Websocket客户端