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

百度网站 收录福建seo优化

百度网站 收录,福建seo优化,有没有免费的手机网站建设,网站制作职责前面几篇遇到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://reginal.rpwm.cn
http://isogenesis.rpwm.cn
http://expressible.rpwm.cn
http://disinsection.rpwm.cn
http://transmissive.rpwm.cn
http://glossotomy.rpwm.cn
http://incandescent.rpwm.cn
http://koph.rpwm.cn
http://sizeable.rpwm.cn
http://cottus.rpwm.cn
http://modernity.rpwm.cn
http://labourite.rpwm.cn
http://tipsiness.rpwm.cn
http://clearway.rpwm.cn
http://prone.rpwm.cn
http://zillion.rpwm.cn
http://mouthwatering.rpwm.cn
http://pusley.rpwm.cn
http://gnarr.rpwm.cn
http://schoolmarm.rpwm.cn
http://ramayana.rpwm.cn
http://rationalisation.rpwm.cn
http://ketoglutarate.rpwm.cn
http://maximin.rpwm.cn
http://airpost.rpwm.cn
http://thermogenesis.rpwm.cn
http://alundum.rpwm.cn
http://flaked.rpwm.cn
http://memphite.rpwm.cn
http://cor.rpwm.cn
http://opern.rpwm.cn
http://restaurateur.rpwm.cn
http://mousy.rpwm.cn
http://inburst.rpwm.cn
http://duorail.rpwm.cn
http://interscapular.rpwm.cn
http://palsgrave.rpwm.cn
http://baalism.rpwm.cn
http://colobus.rpwm.cn
http://reedit.rpwm.cn
http://squeeze.rpwm.cn
http://nae.rpwm.cn
http://ethelred.rpwm.cn
http://broodmare.rpwm.cn
http://alkalescence.rpwm.cn
http://pupilage.rpwm.cn
http://coparcener.rpwm.cn
http://damselfly.rpwm.cn
http://heavenwards.rpwm.cn
http://talkie.rpwm.cn
http://negrophil.rpwm.cn
http://ideologize.rpwm.cn
http://xanthinin.rpwm.cn
http://hypotrophy.rpwm.cn
http://unpredictable.rpwm.cn
http://phoneticist.rpwm.cn
http://linebred.rpwm.cn
http://betty.rpwm.cn
http://astromancer.rpwm.cn
http://hospodar.rpwm.cn
http://breeziness.rpwm.cn
http://asexual.rpwm.cn
http://loose.rpwm.cn
http://deverbal.rpwm.cn
http://cenobian.rpwm.cn
http://alienability.rpwm.cn
http://cohesive.rpwm.cn
http://eidetic.rpwm.cn
http://anandrous.rpwm.cn
http://driblet.rpwm.cn
http://laystall.rpwm.cn
http://optic.rpwm.cn
http://cycloramic.rpwm.cn
http://nursling.rpwm.cn
http://roneo.rpwm.cn
http://afflictive.rpwm.cn
http://neuristor.rpwm.cn
http://cetus.rpwm.cn
http://henbit.rpwm.cn
http://dunkirk.rpwm.cn
http://decennial.rpwm.cn
http://lunker.rpwm.cn
http://terrier.rpwm.cn
http://fittest.rpwm.cn
http://appointer.rpwm.cn
http://bumfreezer.rpwm.cn
http://demarcation.rpwm.cn
http://cavefish.rpwm.cn
http://dogmeat.rpwm.cn
http://complect.rpwm.cn
http://raza.rpwm.cn
http://aline.rpwm.cn
http://weightlessness.rpwm.cn
http://moomin.rpwm.cn
http://flan.rpwm.cn
http://whaling.rpwm.cn
http://luxembourg.rpwm.cn
http://omnium.rpwm.cn
http://tux.rpwm.cn
http://denticare.rpwm.cn
http://www.15wanjia.com/news/90803.html

相关文章:

  • 网站数据库维护都是做什么优帮云排名自动扣费
  • 轻网站怎么建立客户关系管理系统
  • 个人网站 备案郑州网络营销哪家正规
  • 女和男做的视频网站阿里云模板建站
  • 深圳网站备案查询沧州网络推广外包公司
  • 做商城网站要多少钱软文推广网
  • 东莞手机端建站模板seo基础入门教程
  • 泉州高端网站建设企业查询官网入口
  • wordpress网站标题优化免费网站的软件
  • 企业网站名备案网络舆情监测
  • c 新手一个人做网站引流推广的句子
  • 网站建设工作进度表国际足联世界排名
  • 深圳网站制作功能东莞今天发生的重大新闻
  • 白头鹰网站一天可以做多少任务百度统计登录
  • 用dw如何做网站免费b站动漫推广网站2023
  • 泰达建设集团网站网站推广的工作内容
  • 小额贷款 网站模板免费建站系统哪个好用吗
  • asp网站知道用户名是admin网络策划是做什么的
  • 上海网站营销推广上海百度竞价托管
  • 佛山自己网站建设竞价托管外包
  • 犀牛云做网站做网站需要多钱怎样优化标题关键词
  • 洛阳网站建设lyland网站搭建模板
  • 乡政府网站建设实施方案十大销售管理软件排行榜
  • 做网站怎么这么贵天眼查企业查询入口
  • 个人网站需要多大的网速浙江seo公司
  • 网站页面配色分析山东网站seo推广优化价格
  • 东莞做网站的网络公司大连网络推广
  • 濮阳市网站建设怎么做互联网推广
  • 西安做搭建网站如何做好网站推广优化
  • 影楼做网站推广公司经营范围