当前位置: 首页 > news >正文

怎么制作手机网站平台百度推广登录平台网址

怎么制作手机网站平台,百度推广登录平台网址,保护区门户网站建设制度,淮南营销型网站建设怎么样在 react 项目中,setState 被用于更新 state,从而实现组件重新渲染更新。经过查找阅读许多资料以及源码后,本文就来个人总结一下,简要解析 react 在 setState 后是如何更新组件的。 前言: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 后解析。

// 批处理策略
var ReactDefaultBatchingStrategy = {isBatchingUpdates: false,batchedUpdates: function(callback, a, b, c, d, e) {var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;ReactDefaultBatchingStrategy.isBatchingUpdates = true; //开启一次batchif (alreadyBatchingUpdates) {return callback(a, b, c, d, e);} else {// 启动事务, 将callback放进事务里执行return transaction.perform(callback, null, a, b, c, d, e);}},
};

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 :
ReactComponent.prototype.setState = function(partialState, callback) {this.updater.enqueueSetState(this, partialState);if (callback) {// 如果有callback,就将callback放入回调函数队列this.updater.enqueueCallback(this, callback, 'setState');}
};// => ReactUpdateQueue.js:
enqueueSetState: function(publicInstance, partialState) {// 根据 this.setState 中的 this 拿到内部实例, 也就是组件实例var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');// 取得组件实例的_pendingStateQueuevar queue =internalInstance._pendingStateQueue ||(internalInstance._pendingStateQueue = []);// 将partial state存到_pendingStateQueuequeue.push(partialState);// 调用enqueueUpdateenqueueUpdate(internalInstance);}// => ReactUpdate.js:
function enqueueUpdate(component) {ensureInjected(); // 注入默认策略// 如果没有开启batch(或当前batch已结束)就开启一次batch再执行, 这通常发生在异步回调中调用 setState      // 的情况if (!batchingStrategy.isBatchingUpdates) {batchingStrategy.batchedUpdates(enqueueUpdate, component);return;}// 如果batch已经开启就存储更新dirtyComponents.push(component);if (component._updateBatchNumber == null) {component._updateBatchNumber = updateBatchNumber + 1;}
}
3、事务结束时(close阶段)

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

var flushBatchedUpdates = function () {// 启动批量更新事务while (dirtyComponents.length || asapEnqueued) {if (dirtyComponents.length) {var transaction = ReactUpdatesFlushTransaction.getPooled();transaction.perform(runBatchedUpdates, null, transaction);ReactUpdatesFlushTransaction.release(transaction);}
// 批量处理callbackif (asapEnqueued) {asapEnqueued = false;var queue = asapCallbackQueue;asapCallbackQueue = CallbackQueue.getPooled();queue.notifyAll();CallbackQueue.release(queue);}}
};// ReactUpdates.js
function runBatchedUpdates(transaction) {var len = transaction.dirtyComponentsLength;// 排序保证父组件优先于子组件更新dirtyComponents.sort(mountOrderComparator);// 代表批量更新的次数, 保证每个组件只更新一次updateBatchNumber++;// 遍历 dirtyComponentsfor (var i = 0; i < len; i++) {var component = dirtyComponents[i];var callbacks = component._pendingCallbacks;component._pendingCallbacks = null;...// 执行更新ReactReconciler.performUpdateIfNecessary(component,transaction.reconcileTransaction,updateBatchNumber,);...// 存储 callback以便后续按顺序调用if (callbacks) {for (var j = 0; j < callbacks.length; j++) {transaction.callbackQueue.enqueue(callbacks[j],component.getPublicInstance(),);}}}
}
4、updateComponent

ReactReconciler.performUpdateIfNecessary中调用updateComponent更新组件


performUpdateIfNecessary: function (transaction) {if (this._pendingElement != null) {ReactReconciler.receiveComponent(this, this._pendingElement, transaction,                 this._context);} else if (this._pendingStateQueue !== null || this._pendingForceUpdate) {this.updateComponent(transaction, this._currentElement, this._currentElement,             this._context, this._context);} else {this._updateBatchNumber = null;}
}
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

总结
图片

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.prototype.setState = function(partialState, callback) {this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
updater更新器中的enqueueSetState
var updater = {...enqueueSetState(instance, partialState, callback) {// 获取到当前组件实例上的fiberconst fiber = ReactInstanceMap.get(instance);callback = callback === undefined ? null : callback;// 计算当前fiber的到期时间,即计算出优先级const expirationTime = computeExpirationForFiber(fiber);const update = {expirationTime,partialState,callback,isReplace: false,isForced: false,capturedValue: null,next: null,};// 把更新信息任务插入fiber的update queue更新队列中insertUpdateIntoFiber(fiber, update);// 开启调度任务,进入fiber的调度阶段(reconciliation)scheduleWork(fiber, expirationTime);}...
};

enqueueSetState详解:

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

function get(key) {return key._reactInternalFiber;
}

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

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

//用来计算fiber的到期时间,到期时间用来表示任务的优先级。function computeExpirationForFiber(fiber) {var expirationTime = void 0;if (expirationContext !== NoWork) {// expirationTime = expirationContext;} else if (isWorking) {if (isCommitting) {//同步模式,立即处理任务,默认是1expirationTime = Sync;} else {//渲染阶段的更新应该与正在渲染的工作同时过期。expirationTime = nextRenderExpirationTime;}} else {//没有到期时间的情况下,创建一个到期时间if (fiber.mode & AsyncMode) {if (isBatchingInteractiveUpdates) {// 这是一个交互式更新var currentTime = recalculateCurrentTime();expirationTime = computeInteractiveExpiration(currentTime);} else {// 这是一个异步更新var _currentTime = recalculateCurrentTime();expirationTime = computeAsyncExpiration(_currentTime);}} else {// 这是一个同步更新expirationTime = Sync;}}if (isBatchingInteractiveUpdates) {//这是一个交互式的更新。跟踪最低等待交互过期时间。这允许我们在需要时同步刷新所有交互更新。if (lowestPendingInteractiveExpirationTime === NoWork || expirationTime > lowestPendingInteractiveExpirationTime) {lowestPendingInteractiveExpirationTime = expirationTime;}}return expirationTime;}

fiber优先级定义:

module.exports = {  // heigh levelNoWork: 0, // No work is pending.SynchronousPriority: 1, // For controlled text inputs. TaskPriority: 2, // Completes at the end of the current tick.AnimationPriority: 3, // Needs to complete before the next frame.// low levelHighPriority: 4, // Interaction that needs to complete pretty soon to feel responsive.LowPriority: 5, // Data fetching, or result from updating stores.OffscreenPriority: 6, // Won't be visible but do the work in case it becomes visible.
};

3、insertUpdateIntoFiber(fiber, update)

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

function insertUpdateIntoFiber(fiber, update) {//确保更新队列存在,不存在则创建ensureUpdateQueues(fiber);//上一步已经将q1和q2队列进行了处理,定义2个局部变量queue1和queue2来保存队列信息。var queue1 = q1;var queue2 = q2;// 如果只有一个队列,请将更新添加到该队列并退出。if (queue2 === null) {insertUpdateIntoQueue(queue1, update);return;}// 如果任一队列为空,我们需要添加到两个队列中。if (queue1.last === null || queue2.last === null) {//将update的值更新到队列1和队列2上,然后退出该函数insertUpdateIntoQueue(queue1, update);insertUpdateIntoQueue(queue2, update);return;}// 如果两个列表都不为空,则由于结构共享,两个列表的最后更新都是相同的。所以,我们应该只追加到其中一个列表。insertUpdateIntoQueue(queue1, update);// 但是我们仍然需要更新queue2的`last`指针。queue2.last = update;
}

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 渲染整理

http://www.hengruixuexiao.com/news/40818.html

相关文章:

  • 婚庆网站策划书制作公司网站的步骤
  • 承德建设工程信息网站信息流广告投放平台
  • 青岛 生物类网站建设百度网络营销中心客服电话
  • 网站开发服务项目百度关键词搜索怎么收费
  • 贵阳市建设厅官方网站厦门网站综合优化贵吗
  • 惠州网站建设西安网站公司推广
  • 站长素材网站无锡网站建设优化公司
  • 网站制作的流程新东方英语培训机构官网
  • 2017网站开发新技术国内的搜索引擎排名
  • 如何做优化网站的原创性文章it培训机构排名
  • 网站怎么申请百度小程序全国最新疫情实时状况地图
  • 做一个简单网站多少钱全网推广平台推荐
  • 未来做那个网站能致富市场营销策划案的范文
  • 网站建设做的快福州seo网站推广优化
  • 门户网站建设技术要求如何自己开网站
  • 制作公司网站源代码怎么弄北京网络优化
  • 制作网站公司b站是哪个网站
  • 国外疫情最新消息今天seo怎么优化排名
  • 自己做网站好做吗优化人员配置
  • 有那些专门做职业统计的网站长治seo顾问
  • 网站做跳转怎么做广东网约车涨价
  • 合川网站建设公司爱站网长尾关键词
  • 东莞网站推广网站维护工程师
  • 做网站的钱付款用途写什么企业营销策划有限公司
  • 俄文网站开发地点唐山seo排名优化
  • 完整网站设计超八成搜索网站存在信息泄露问题
  • 我的网站怎么做权重查询爱站网
  • 防水网站建设产品线上营销推广方案
  • 做网站的流程知乎优化推广seo
  • 做原型网站互动网站建设