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

相关文章

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

利用python实现对excel文件进行加密

《利用python实现对excel文件进行加密》由于文件内容的私密性,需要对Excel文件进行加密,保护文件以免给第三方看到,本文将以Python语言为例,和大家讲讲如何对Excel文件进行加密,感兴... 目录前言方法一:使用pywin32库(仅限Windows)方法二:使用msoffcrypto-too