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

百度网站 收录游戏推广员平台

百度网站 收录,游戏推广员平台,独立外贸网站建设,如何开自己的网站前面几篇遇到updateQueue的时候,我们把它先简单的当成了一个队列处理,这篇我们来详细讨论一下这个更新队列。 有关updateQueue中的部分,可以见源码 UpdateQueue实现 Update对象 我们先来看一下UpdateQueue中的内容,Update对象&…

前面几篇遇到updateQueue的时候,我们把它先简单的当成了一个队列处理,这篇我们来详细讨论一下这个更新队列。 有关updateQueue中的部分,可以见源码  UpdateQueue实现

Update对象

我们先来看一下UpdateQueue中的内容,Update对象,其实现如下:

/** 更新的Action 可以是State 也可以是函数 */
export type Action<State> = State | ((prevState: State) => State);
/** 定义Dispatch函数 */
export type Dispatch<State> = (action: Action<State>) => void;/** 更新对象 */
export class Update<State> {next: Update<State>;action: Action<State>;lane: Lane; // 当前更新的优先级Laneconstructor(action: Action<State>, lane: Lane) {this.action = action;this.next = null;this.lane = lane;}
}

其中,包含

  • action: Action对象,可以是任意类型,对应的我们在setState中传入的参数,如果传入一个函数,对应的是函数类型action,则运行函数得到状态值。如果不是函数,则直接将其作为状态值。
  • lane: 当前更新对应的优先级lane
  • next: 涉及到updateQueue的数据结构,指向下一个Update对象 

我们在很多地方都需要创建更新对象,比如dispatchSetState是,即你修改状态的时候

 初始化的时候,在updateContainer中,也会创建update对象

updateQueue - 环形链表 

updateQueue本质上是一个存储Update对象的数据结构,但是其不是一个普通的数组,其内部实现了一个环形链表用来存储Update对象,其定义如下

export class UpdateQueue<State> {shared: {pending: Update<State> | null;};/** 派发函数 */dispatch: Dispatch<State>;/** 基础队列 */baseQueue: Update<State> | null;/** 基础state */baseState: State;
...
}

其内部包含shared属性,指向一个对象,对象中包含pending对象,指向Update对象,如下图所示

其中,Update对象的next指针指向下一个Update对象,其组成一个环形链表,如图所示:

其中:

  • updateQueue.shared.pending指向最后一个Update节点
  • updateQueue.shared.pending.next 为第一个Update节点 

 为什么使用环形链表?

这里使用环形链表的一个好处是,其可以很方便的找到首位元素,可以方便的遍历链表,也可以方便的对两个链表进行拼接,这个在后面的baseQueue 和 baseState逻辑中会用到。

 enqueue入队

enqueue为UpdateQueue的类方法,其作用就是给队列插入Update对象,其实现如下:

 /** 入队,构造环状链表 */enqueue(update: Update<State>, fiber: FiberNode, lane: Lane) {if (this.shared.pending === null) {// 插入第一个元素,此时的结构为// shared.pending -> firstUpdate.next -> firstUpdateupdate.next = update;this.shared.pending = update;} else {// 插入第二个元素update.next = this.shared.pending.next;this.shared.pending.next = update;this.shared.pending = update;}/** 在当前的fiber上设置lane */fiber.lanes = mergeLane(fiber.lanes, lane);/** 在current上也设置lane 因为在beginwork阶段 wip.lane = NoLane 如果bailout 需要从current恢复 */const current = fiber.alternate;if (current) {current.lanes = mergeLane(current.lanes, lane);}}

我们用一个插入队列来演示插入过程:

// 假设有插入队列
enqueue(100)
enqueue(current => current + 1)
enqueue(200)

插入100, 100对应的pending.next指向自己,此时100对应的Update又是首节点也是尾节点

插入curr=>curr+1的update节点,此时首节点为pending.nexy也就是 curr=>curr+1 尾节点为100

插入200节点,此时首节点为200 尾节点为100 都是从pending.next的位置插入,如图

设置lane

enqueue方法除了传入更新对象,还需要传入更新所发生在的Fiber对象和对应的更新lane,其目的是在当前更新的Fiber上记录lane,其逻辑如下:

    /** 在当前的fiber上设置lane */fiber.lanes = mergeLane(fiber.lanes, lane);/** 在current上也设置lane 因为在beginwork阶段 wip.lane = NoLane 如果bailout 需要从current恢复 */const current = fiber.alternate;if (current) {current.lanes = mergeLane(current.lanes, lane);}

可以看到,当前更新的fiber节点的alternate节点的lanes也被设置了,这是为了先保存当前的lanes方便后面中短渲染 如bailout的时候能恢复当前fiber的lanes

processQueue - 处理更新

process函数的作用就是处理当前队列的所有更新,在不考虑优先级的情况下,其实现可以简化为如下代码:

  /** 处理任务 */process() {// 当前遍历到的updatelet memorizedState;let currentUpdate = this.baseQueue?.next;if (currentUpdate) {do {const currentAction = currentUpdate.action;if (currentAction instanceof Function) {/** Action是函数类型 运行返回newState */memorizedState = currentAction(memorizedState);} else {/** 非函数类型,直接赋给新的state */memorizedState = currentAction;}currentUpdate = currentUpdate.next;} while (currentUpdate !== this.baseQueue?.next);}return  memorizedState;}

即循环遍历整个环状链表,对action的类型进行检测,如果是函数则运行,如果是非函数直接把ation赋给memorizedState,最后将memorizedState返回即可! 

引入优先级lane

如果加入优先级lane的处理逻辑,process的处理逻辑会稍微有些复杂,我们看个例子

onClick={()=>{// 同步更新Lane = 1setvariable(100)startTransition(()=>{// 可以理解为 创建一个优先级lane=8的UpdatesetVariable(curr=>curr+100)    })// 同步更新Lane = 1setVariable(curr => curr + 100)}}

在一个onClick函数中,我们设置了三次setVariable函数,其中,第二次setter使用startTranstion包裹,这个函数由useTranstion hook提供,这个后面再讲,你可以先理解为,在这个startTransition包裹的setter对应的优先级都会被改成 8 即可 TransitionLane

此时,variable hook中的updateQueue对应的shared.pending队列如下:

由于队列中的优先级不同,我们一次只处理一个优先级的Update对象,对于其他优先级的对象需要进行跳过。

但是需要注意,被我们跳过的更新需要在后面的更新中被执行,并且,虽然我们通过优先级把一次更新拆分成了两次更新,但是最终的结果需要是一样的。

比如,第一次更新 

执行 action 100

跳过 curr=>curr+100 并且记住此时的状态100

执行curr => curr + 200

此时的结果为 300

第二次更新,需要从上次执行到的位置重新执行

执行curr=>curr+100 结果为200

执行 curr=>curr+200 (虽然此Update执行过了,但是为了保证结果一致,还需执行)结果为400

注意,虽然拆成了两次更新,但是最终更新的结果一定是和不加startTranstion按顺序执行的结果一样的!

这样我们就可以把高耗时的更新操作设置低优先级,先处理低耗时的更新,同时保证最终结果不变。

实现这样逻辑的算法如下:

准备一个memorizedState,记录当前updateQueue的状态值

准备一个baseState 用来记录第一个 跳过第一个Update时的状态值

准备一个baseQueue,用来记录本次更新跳过的更新对象 和 跳过更新之后的更新对象, 下一次更新就用这个baseQueue中的Update

遍历队列元素,使用isSubsetOfLanes来判断当前Update.lane是不是等于当前正在更新的lane(wipRenderedLane)

如果是则看baseQueue队列

   如果baseQueue队列为空, 则执行action,给memorizedState赋值

   如果baseQueue队列不为空, 则说明当前更新前面,已经有跳过的Update被加入到baseQueue了,那么其后面所有的Update对象都要加入baseQueue,则把当前Update对象克隆一份,并且设置优先级为Nolanes,以保证下次更细当前Update一定能被执行,推入baseQueue

并且,由于当前Update的lane是满足的,需要执行action,更新memorizedState

如果不是, 看updateQueue队列

 如果队列为空,此时为第一个跳过的Update对象,把当前的Update对象克隆一 份push到baseQueue中,并且把当前memorizedState赋给baseState,记录本次更新第一个跳过Update对应的状态,下次更新就从此开始

如果队列不为空,和上面一样,区别就是不赋baseState了,注意baseState只有第一次更新才设置

最后返回 memorizedState 并且把baseState baseQueue记录在当前updateQueue对象上,复习一下UpdateQueue的ts定义。

export class UpdateQueue<State> {shared: {pending: Update<State> | null;};/** 派发函数 */dispatch: Dispatch<State>;/** 基础队列 */baseQueue: Update<State> | null;/** 基础state */baseState: State;
...
}

下面我们画图来解释一下 Update队列如下:

Update List 
[action: 100,lane: 1]
[action: curr => curr + 100, lane: 8]
[action: curr = curr+ 200,lane: 1]

此时的updateQueue和状态如下: 

此时的root.pendinglanes 包含lane1 和 lane8 即SyncLane和TranstionLane

开始更新最高的优先级lane1 , 处理第一个Update,由于满足优先级,直接计算并且更新memorizedState = 100

继续处理到curr=>curr+100 此时lane=8 需要跳过,但是此时baseQueue为空,为第一个跳过的更新,需要baseState记录跳过之前的memorizedState = 100,并且克隆一份Update 推入baseQueue

 

继续处理curr=>curr+200 此时满足lane=1 但是由于baseQueue已经不为空,则后面所有的Update无论什么优先级,都需要克隆一份Update对象并且设置lanes为NoLane 推入baseQueue

同时需要计算action更新memorizedState为300

 

第一轮更新结束,此时状态为300,保存baseState和baseQueue并且删除shared.pending队列,因为已经用不上了。

第二轮更新 lane=8 此时从baseQueue中取出上次跳过的更新,继续处理,此时memorizedState被baseState初始化为100

 处理第一个更新,此时memorizedState=200

处理第二个更新,由于是任意Lanes&NoLanes === NoLanes 所以第二个update也满足优先级,更新memorizedState=400 此时完成更新 

 最终结果为400

两次更新,第一次更新值为300 第二次更新值为400 做到了过渡的作用

如果页面中包含逻辑,如果variable === 400 则渲染10000个li 此时如果不用startTranstion降低优先级,则更新variable到400的那次更新的优先级lane=1 那么此时如果有更高优先级任务来,则此次lane=1的更新无法被打断,导致页面卡住不动 影响用户体验。

如果更新到400的更新优先级为8 那么当更高优先级更新来的时候,此次大规模的更新会被打断,优先执行更高优先级更新(比如用户事件) 在高优先级任务执行完成之后,再执行这个大规模更新渲染,优化了用户体验!

连接baseQueue和pending

每一轮更新之后,pending对应的update环会被清空,但是当处理本次更新的时候,又有新的update被挂上,此时baseQueue和pending都有值

比如,在某次更新的useEffect中,设置了setVariable 此时的更新队列中又有新的更新了

此时就需要把baseQueue队列和pending队列连接,baseQueue队列在前

需要定义两个变量 baseFirst 和 pendingFirst 分别指向baseQueue和pending的对头,因为改变过pending/baseQueue.next 之后 就无法直接找到队头元素

第一步 设置baseQueue.next = pendingFirst 把baseQueue尾和pending头连接 如图

 第二步 Pending.next = baseFirst 此时pending队列的尾和baseQueue头连接 如图

此时 baseFirst 就是整个队列的头部了

说完了原理,我们看一下process方法的完整实现:

  /** 处理任务 */process(renderLane: Lane, onSkipUpdate?: (update: Update<any>) => void) {/** 获取baseQueue pending 完成拼接 */let baseState = this.baseState;let baseQueue = this.baseQueue;const currentPending = this.shared.pending;// 生成新的baseQueue过程if (currentPending !== null) {if (baseQueue !== null) {// 拼接两个队列// pending -> p1 -> p2 -> p3const pendingFirst = currentPending.next; // p1// baseQueue -> b1->b2->b3const baseFirst = baseQueue.next; // b1// 拼接currentPending.next = baseFirst; // p1 -> p2 -> p3 -> pending -> b1 -> b2 -> b3baseQueue.next = pendingFirst; //b1-> b2 -> b3 -> baseQueue -> p1 -> p2 -> p3// p1 -> p2 -> p3 -> pending -> b1 -> b2 -> b3 baseQueue}// 合并 此时 baseQueue -> b1 -> b2 -> b3 -> p1 -> p2 -> p3baseQueue = currentPending;// 覆盖新的baseQueuethis.baseQueue = baseQueue;// pending可以置空了this.shared.pending = null;}// 消费baseQueue过程// 设置新的basestate和basequeuelet newBaseState: State = baseState;let newBaseQueueFirst: Update<State> | null = null;let newBaseQueueLast: Update<State> | null = null;// 新的计算值let memorizedState: State = baseState;// 当前遍历到的updatelet currentUpdate = this.baseQueue?.next;if (currentUpdate) {do {const currentUpdateLane = currentUpdate.lane;// 看是否有权限if (isSubsetOfLanes(renderLane, currentUpdateLane)) {// 有权限if (newBaseQueueFirst !== null) {// 已经存在newBaseFirst 则往后加此次的update 并且将此次update的lane设置为NoLane 保证下次一定能运行const clone = new Update(currentUpdate.action, NoLane);newBaseQueueLast = newBaseQueueLast.next = clone;}if (currentUpdate.hasEagerState) {memorizedState = currentUpdate.eagerState;} else {// 不论存不存在newBaseFirst 都要计算memorizedStateconst currentAction = currentUpdate.action;if (currentAction instanceof Function) {/** Action是函数类型 运行返回newState */memorizedState = currentAction(memorizedState);} else {/** 非函数类型,直接赋给新的state */memorizedState = currentAction;}}} else {// 无权限const clone = new Update(currentUpdate.action, currentUpdate.lane);if (onSkipUpdate) {onSkipUpdate(clone);}// 如果newBaseQueueFirst === null 则从第一个开始添加newbaseQueue队列if (newBaseQueueFirst === null) {newBaseQueueFirst = newBaseQueueLast = clone;// newBaseState到此 不在往后更新 下次从此开始newBaseState = memorizedState;} else {newBaseQueueLast = newBaseQueueLast.next = clone;}}currentUpdate = currentUpdate.next;} while (currentUpdate !== this.baseQueue?.next);}if (newBaseQueueFirst === null) {// 此次没有update被跳过,更新newBaseStatenewBaseState = memorizedState;} else {// newbaseState不变 newBaseQueueFirst newBaseQueueLast 成环newBaseQueueLast.next = newBaseQueueFirst;}// 保存baseState和BaseQueuethis.baseQueue = newBaseQueueLast;this.baseState = newBaseState;return { memorizedState };}

 


文章转载自:
http://dinncophlebosclerosis.ydfr.cn
http://dinncooni.ydfr.cn
http://dinncocroydon.ydfr.cn
http://dinncoimpossibility.ydfr.cn
http://dinncolex.ydfr.cn
http://dinncoamaranth.ydfr.cn
http://dinncomalingery.ydfr.cn
http://dinncobred.ydfr.cn
http://dinncothumbstall.ydfr.cn
http://dinncomater.ydfr.cn
http://dinncoathetoid.ydfr.cn
http://dinncoguevarist.ydfr.cn
http://dinncospeciously.ydfr.cn
http://dinncopunkin.ydfr.cn
http://dinncophotojournalism.ydfr.cn
http://dinncofarming.ydfr.cn
http://dinncorage.ydfr.cn
http://dinncohouting.ydfr.cn
http://dinncocomputerizable.ydfr.cn
http://dinncoechelette.ydfr.cn
http://dinncoteacherage.ydfr.cn
http://dinncodahabeeyah.ydfr.cn
http://dinncoringing.ydfr.cn
http://dinncodwarfism.ydfr.cn
http://dinncoburp.ydfr.cn
http://dinncogrieved.ydfr.cn
http://dinncosemiprivate.ydfr.cn
http://dinncofibrocartilage.ydfr.cn
http://dinncoimpendence.ydfr.cn
http://dinncodryness.ydfr.cn
http://dinncomammilla.ydfr.cn
http://dinncocardiorespiratory.ydfr.cn
http://dinncofaithworthy.ydfr.cn
http://dinncounawakened.ydfr.cn
http://dinncowaur.ydfr.cn
http://dinncoesthonia.ydfr.cn
http://dinncopapacy.ydfr.cn
http://dinncounderfed.ydfr.cn
http://dinncodimm.ydfr.cn
http://dinncoconestoga.ydfr.cn
http://dinncodefend.ydfr.cn
http://dinncophylloxera.ydfr.cn
http://dinncogymnasium.ydfr.cn
http://dinncoxiv.ydfr.cn
http://dinncomitogenic.ydfr.cn
http://dinncohighgate.ydfr.cn
http://dinncomodelletto.ydfr.cn
http://dinncobialy.ydfr.cn
http://dinncobuttonhole.ydfr.cn
http://dinncofishgarth.ydfr.cn
http://dinncodefamation.ydfr.cn
http://dinncoprefiguration.ydfr.cn
http://dinncorusa.ydfr.cn
http://dinncodayton.ydfr.cn
http://dinncomouthwash.ydfr.cn
http://dinncovarmint.ydfr.cn
http://dinncolambda.ydfr.cn
http://dinncokinaesthesis.ydfr.cn
http://dinncoworldful.ydfr.cn
http://dinncogelidity.ydfr.cn
http://dinncodivisor.ydfr.cn
http://dinncobargeman.ydfr.cn
http://dinncosupersensitive.ydfr.cn
http://dinncolallan.ydfr.cn
http://dinncosolanum.ydfr.cn
http://dinncoposthole.ydfr.cn
http://dinncohieroglyphical.ydfr.cn
http://dinncoinsidious.ydfr.cn
http://dinncofur.ydfr.cn
http://dinncospeculative.ydfr.cn
http://dinncodevonshire.ydfr.cn
http://dinncoparainfluenza.ydfr.cn
http://dinncodoyley.ydfr.cn
http://dinncoaccommodation.ydfr.cn
http://dinncobadman.ydfr.cn
http://dinncodevilled.ydfr.cn
http://dinncobratwurst.ydfr.cn
http://dinncodisrelish.ydfr.cn
http://dinncomeliorable.ydfr.cn
http://dinncoxylanthrax.ydfr.cn
http://dinncosociopathic.ydfr.cn
http://dinncoarithmometer.ydfr.cn
http://dinncospatterdock.ydfr.cn
http://dinncoepicist.ydfr.cn
http://dinncocarlovingian.ydfr.cn
http://dinncocatena.ydfr.cn
http://dinncoproverbially.ydfr.cn
http://dinncoophiolater.ydfr.cn
http://dinncoavoirdupois.ydfr.cn
http://dinncolaccolith.ydfr.cn
http://dinncosemiyearly.ydfr.cn
http://dinncoacarpous.ydfr.cn
http://dinncoisro.ydfr.cn
http://dinncophosphorolytic.ydfr.cn
http://dinncohummock.ydfr.cn
http://dinncoarcograph.ydfr.cn
http://dinncosolenoid.ydfr.cn
http://dinncodimeter.ydfr.cn
http://dinncoasphodel.ydfr.cn
http://dinncodamnify.ydfr.cn
http://www.dinnco.com/news/98054.html

相关文章:

  • 学校网站建设开发方案书如何做好企业推广
  • 用asp.net做后台网站网络服务商电话
  • 下载的字体如何安装到wordpress深圳网站建设推广优化公司
  • 做黄金的经常看什么网站网站怎么制作免费的
  • 如何做网站旅游产品分析网络营销策划公司
  • 如何做一张网站平面效果图网店推广方法有哪些
  • 购物平台网站建设流程企业管理培训班
  • 做网站拍幕布照是什么意思百度免费下载安装
  • 做3d打印网站雅虎日本新闻
  • 网站开发功能需求文档北京谷歌seo
  • 网站上的链接怎么做美国站外推广网站
  • 安徽网站开发费用营销渠道模式有哪些
  • 温州营销网站制作费用百度热词指数
  • 做简单网站用什么软件广东互联网网络营销推广
  • 域名备案怎么关闭网站百度大全下载
  • 韩城网站建设网络营销专业
  • 政务网站的建设时期的概述最新互联网项目平台网站
  • 最火爆的网页游戏站优化
  • 公司做网站费用计什么科目淄博头条新闻今天
  • 平和网站建设亚马逊站外推广网站
  • 龙港做网页网站制作网络营销的5种营销方式
  • wordpress设置谷歌验证seo排名赚能赚钱吗
  • 秦皇岛做网站公司排名如何做网页链接
  • 苹果手机做微电影网站有哪些内容代刷网站推广
  • 郑州响应式网站不受限制的搜索引擎
  • 网站建设完整教程视频教程谷歌浏览器官网
  • 博客页面html模板乐云seo官网
  • 服务项目网站建设永久域名查询
  • python购物网站开发流程上海关键词优化按天计费
  • 智能锁东莞网站建设淘宝seo软件