js等待一个方法结束后执行下个方法_react15 和 react16 在 setState 后的更新渲染解析...

在 react 项目中,setState 被用于更新 state,从而实现组件重新渲染更新。经过查找阅读许多资料以及源码后,本文就来个人总结一下,简要解析 react 在 setState 后是如何更新组件的。

前言:setState 的同步和异步

  1. 异步:setState 一般情况下是异步的,由 react 的批量更新事务(ReactDefaultBatchingStrategy)控制(即 react 控制的事件,非调用 js 原生事件时是异步的),以及生命周期函数调用 setState 也不会同步更新 state。同一个函数中执行多个 setState 时会合并,并且同一个属性以最后一次 setState 的值为准。
  2. 同步:setState 在 setTimeout,setInterval 和 js 原生事件中被调用,则是同步的。

原理:同步和异步是由什么控制的? 是通过 batchedUpdates 函数中设置 isBatchingUpdates 为 true 时,则 setState 为异步更新,false 时为同步更新。

react15

先介绍事务(ReactDefaultBatchingStrategyTransaction)和 batchedUpdates 函数(用来启动事务的,启动事务后 setState 就是异步更新了),后介绍setState。

ReactDefaultBatchingStrategyTransaction 事务

setState 依靠事务进行更新,事务生命周期包含 initialize、perform、close 阶段。在开启事务后,遇到 setState 后 则将 partial state 存到组件实例的_pendingStateQueue 上, 接着调用 enqueueUpdate 排队更新方法,如下 setState 后解析。
// 批处理策略

batchUpdates

batchedUpdates先设置 isBatchingUpdates为true(ReactDefaultBatchingStrategy.isBatchingUpdates = true)开启事务,后将 callback函数放进事务里执行(transaction.perform(callback,...)),无论你传进去的函数是什么, 无论这个函数后续会做什么, 都会在执行完 callback(setState 的第二个参数)后调用事务的 close 方法

在 React 中,调用batchedUpdates有很多地方

第一种情况:首次渲染组件时(源码:在ReactMount.js里调用了ReactUpdates.batchedUpdates) 第二种情况:元素上或者组件上绑定了react控制的事件(非调用js原生事件),事件的监听函数中调用setState。(源码:在ReactEventListener.js里,react事件系统中的dispatchEvent函数启动了事务(调用了ReactUpdates.batchedUpdates))

重点:setState后

1、setState后

调用updater的enqueueSetState方法把需要更新的state(partial state)push进去等待队列_pendingStateQueue中, 接着调用enqueueUpdate排队更新。

2、enqueueUpdate

enqueueUpdate方法里需要判断batchingStrategy.isBatchingUpdates == true,即是否开启batch事务 情况一:如果已经开启batch,然后标记当前组件为dirtyComponent, 存到dirtyComponents数组中,等到 ReactDefaultBatchingStrategy事务结束时(close)调用runBatchedUpdates批量更新所有组件 情况二:方法中如果没有开启batch(或当前batch已结束,也就是说在事务的initialize或更新阶段)就调用batchedUpdates函数开启一次batch,再重新执行enqueueUpdate方法,判断isBatchingUpdates,现在为true了,标记当前组件为dirtyComponent, 存到dirtyComponents数组中, 并没有立即更新,而是继续执行后面事情,等到 ReactDefaultBatchingStrategy事务结束时(close)调用flushBatchedUpdates函数=>runBatchedUpdates函数批量更新所有组件(第三点)

// ReactBaseClasses.js :

3、事务结束时(close阶段)

批量更新的阶段,调用flushBatchedUpdates函数启动ReactUpdatesFlushTransaction事务负责批量更新,这个事务执行了runBatchedUpdates方法通过遍历dirtyComponents数组(在函数里ReactReconciler.performUpdateIfNecessary中调用updateComponent更新组件)进行批量更新。再结束本次batch事务(即ReactDefaultBatchingStrategy.isBatchingUpdates = false; )

var 

4、updateComponent

ReactReconciler.performUpdateIfNecessary中调用updateComponent更新组件

performUpdateIfNecessary

5、updateComponent

react内部有3种不同组件:ReactCompositeComponent、ReactDOMComponent和ReactDOMTextComponent。 ReactCompositeComponent:re-render, 与之前 render 的 element 比较, 如果两者key && element.type 相等, 则进入下一层进行更新; 如果不等, 直接移除重新mount ReactDOMComponent: render前生成新的虚拟DOM,对前后虚拟DOM进行diff算法比较(传送门:react的diff算法),找出最小更新部分,批量更新 * ReactDOMTextComponent:直接更新Text

总结

d5dea271f1bcf35fb87fa26ea5dc01c0.png

react16:加入Fiber

react16版本中加入了Fiber架构(传送门:Fiber简介) Fiber主要分两个阶段

调度阶段(reconciliation):Fiber调度 1. 将一个state更新任务拆分成多个时间小片,形成一个 Fiber 任务队列. 2. 在任务队列中选出优先级高的 Fiber 执行,如果执行时间超过了deathLine,则设置为pending状态挂起状态(即执行一段时间,会跳出找Fiber任务队列中更高级的任务,如果有就放弃当前任务,即使当前任务执行了一半,可能已经经历了一些生命周期,都会被打断从来)。
渲染阶段(commit): 1. 进入render函数,构建真实的virtualDomTree,React将其所有的变更一次性更新到DOM上。

重点:setState后

调用updater的enqueueSetState方法,传入state, callback

Component

updater更新器中的enqueueSetState

var 

enqueueSetState详解:

1、get获取组件实例上的fiber

function 

2、计算到期时间/优先级computeExpirationForFiber

计算当前fiber的优先级(即过期时间),expirationTime 优先级 expirationTime 不为 1 的时候,则其值越低,优先级越高。Fiber任务的优先级:文本框输入 > 本次调度结束需完成的任务 > 动画过渡 > 交互反馈 > 数据更新 > 不会显示但以防将来会显示的任务。如下:
//用来计算fiber的到期时间,到期时间用来表示任务的优先级。

fiber优先级定义:

module

3、insertUpdateIntoFiber(fiber, update)

把更新信息任务插入update queue更新队列中。确保更新队列存在,不存在则调用ensureUpdateQueues(fiber)创建一个fiber队列,调用insertUpdateIntoQueue(queue, update)将update的值更新到queue队列中。
function 

4、scheduleWork(fiber, expirationTime)

开启调度任务,进入fiber的调度阶段 scheduleWork执行流程:scheduleWork => scheduleWorkImpl => requestWork => 同步/异步 => performSyncWork => performWork => performWorkOnRoot => renderRoot/completeRoot => workLoop => performUnitOfWork => beginWork/completeUnitOfWork => updateClassComponent => reconcileChildrenAtExpirationTime => reconcileChildFibers => reconcileChildrenArray

scheduleWork执行流程比较长,下面讲下主要步骤(详细步骤代码):

  1. scheduleWorkImpl: 调用scheduleWorkImpl(fiber, expirationTime, false)函数更新每个node的优先级(即将一个state更新任务拆分成多个时间小片,形成一个 Fiber 任务队列)
  2. requestWork: 同步执行performSyncWork,异步执行scheduleCallbackWithExpiration, scheduleCallbackWithExpiration会调浏览器的requestidlecallback,在浏览器空闲的时候进行处理。 expirationTime:同步执行任务下expirationTime为0,即nowork=0。不为0时,使用requestidlecallback/requestAnimationFrame异步执行任务
  3. performSyncWork 主要的任务调度: 这里会找到高优任务先执行。 同步任务会直接调用performWorkOnRoot进行下一步, 异步任务也会调performWorkOnRoot,但处理不太一样 如果有上次遗留的任务,留到空闲时运行
  4. workLoop(fiber里的判断时间片,超过deathLine,则设置为pending状态): 异步任务在处理的时候会调用shouldYield,shouldYield会判断是不是已经超时了,超时暂时先不做。
  5. performUnitOfWork (reconcilation阶段) 调用beginWork处理组件,针对不同组件不同处理。此过程包括dom diff(生成新的 Virtual DOM,然后通过 Diff 算法,快速找出需要更新的元素,放到更新队列中去,得到新的更新队列)。然后调用completeUnitOfWork对begin work产生的effect list进行一些处理,包括对ReactDOMComponent DOM组件的更新。

渲染阶段(commit)

进入render函数,构建真实的virtualDomTree,调用completeRoot/commitRoot,React将其所有的变更一次性更新到DOM上。

本文参考

从源码全面剖析 React 组件更新机制

React16——看看setState过程中fiber干了什么事情

react 16 渲染整理

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值