React调度的原理是什么
本文讲解"React调度的原理是什么",希望能够解决相关问题。
异步调度
问题:由于对于大型的 React 应用,会存在一次更新,递归遍历大量的虚拟 DOM ,造成占用 js 线程,使得浏览器没有时间去做一些动画效果,伴随项目越来越大,项目会越来越卡。
对比Vue:
Vue 有这 template 模版收集依赖的过程,轻松构建响应式,使得在一次更新中,Vue 能够迅速响应,找到需要更新的范围,然后以组件粒度更新组件,渲染视图。
React 中,一次更新 React 无法知道此次更新的波及范围,所以 React 选择从根节点开始 diff ,查找不同,更新这些不同。
解决:
把 React 的更新,交给浏览器自己控制,浏览器先执行绘制任务,空闲时间执行更新任务,解决了卡顿问题。即采用异步调度的方法。
时间分片
React让浏览器控制React更新:浏览器每执行一次事件循环都会:处理事件,执行 js,调用requestAnimation,布局 Layout,绘制 Paint,在一次执行后,浏览器进入空闲时,可以执行更新任务
谷歌浏览器提供的一个 API, 在浏览器有空余的时间,浏览器就会调用 requestIdleCallback 的回调。
requestIdleCallback(callback,{ timeout })
- callback 回调。浏览器空余时间执行回调函数。
- timeout 超时时间。如果浏览器长时间没有空闲,那么回调就不会执行,为了解决这个问题,可以通过 requestIdleCallback 的第二个参数指定一个超时时间。
React 为了防止 requestIdleCallback 中的任务由于浏览器没有空闲时间而卡死,所以设置了 5 个优先级。
- Immediate -1 需要立刻执行。
- UserBlocking 250ms 超时时间250ms,一般指的是用户交互。
- Normal 5000ms 超时时间5s,不需要直观立即变化的任务,比如网络请求。
- Low 10000ms 超时时间10s,肯定要执行的任务,但是可以放在最后处理。
- Idle 一些没有必要的任务,可能不会执行。
模拟requestdleCallback
条件:
- 可以主动让出主线程,让浏览器去渲染视图。
- 一次事件循环只执行一次,因为执行一个以后,还会请求下一次的时间片。
宏任务:在下次事件循环中执行,不会阻塞浏览器更新。且浏览器一次只会执行一个宏任务。
1、采用setTimeout(fn, 0),间隔时间会变成 4 毫秒左右,不是最优选方案
2、采用MessageChannel 接口,允许开发者创建一个新的消息通道,并通过它的两个 MessagePort 属性发送数据。
在一次更新中,向浏览器请求执行更新任务,调用 requesetHostCallbcak,将更新任务 函数callback赋值给 scheduleHostCallback,port2 向 port1 发起 postMessage消息通知。
port1 会通过 onmessage,接受来自 port2 消息,执行更新任务 scheduleHostCallback,执行完后,清空任务。
异步调度原理
React 发生一次更新,会统一走 ensureRootIsScheduled(调度应用)
对于 正常更新 会走 performSyncWorkOnRoot 逻辑,最后会走 workLoopSync 。
对于 低优先级的异步更新 会走 performConcurrentWorkOnRoot 逻辑,最后会走 workLoopConcurrent 。
区别:异步模式会调用一个 shouldYield(),如果当前浏览器没有空余时间, shouldYield 会中止循环,直到浏览器有空闲时间后再继续遍历,从而达到终止渲染的目的。解决了一次性遍历大量的 fiber ,导致浏览器没有时间执行一些渲染任务,导致了页面卡顿。
1、scheduleCallback
更新任务、异步更新任务都是由调度器 scheduleCallback 统一调度的
正常更新任务:
scheduleCallback(Immediate,workLoopSync)
异步更新任务:
/* 计算超时等级,就是如上那五个等级 */ var priorityLevel = inferPriorityFromExpirationTime(currentTime, expirationTime); scheduleCallback(priorityLevel,workLoopConcurrent)
scheduleCallback() 函数执行过程
scheduleCallback 流程如下:
- 创建一个新的任务 newTask。
- 通过任务的开始时间( startTime ) 和 当前时间( currentTime ) 比较:当 startTime > currentTime, 说明未过期,存到 timerQueue,当 startTime <= currentTime,说明已过期, 存到 taskQueue。
- 如果任务没有过期,用 requestHostTimeout 延时执行 handleTimeout。
- 如果任务过期,并且没有调度中的任务,那么调度 requestHostCallback。
- 本质上调度的是 flushWork。
2、requestHostTimeout
通过 setTimeout 来进行延时指定时间的。
延时执行 handleTimeout,cancelHostTimeout 用于清除当前的延时器。
3、handleTimeout
延时时间后,handleTimeout 会把任务重新放在 requestHostCallback 调度。
通过 advanceTimers 将 timerQueue 中过期的任务转移到 taskQueue 中。然后调用 requestHostCallback 调度过期的任务。
4、advanceTimers
如果任务已经过期,那么将 timerQueue 中的过期任务,放入 taskQueue。
5、flushWork
requestHostCallback ,放入 MessageChannel 中的回调函数是flushWork。
flushWork 如果有延时任务执行的话,那么会先暂停延时任务,然后调用 workLoop ,去真正执行超时的更新任务。
6、workLoop
workLoop 是调度中的 workLoop
React 的更新任务最后都是放在 taskQueue 中
workLoop 会依次更新过期任务队列中的任务
调度流程图
关于 "React调度的原理是什么" 就介绍到此。希望多多支持编程宝库。
jquery如何判断tr是第几行:本文讲解"jquery怎么判断tr是第几行",希望能够解决相关问题。在jquery中,可以利用index()方法来判断指定tr元素是第几行,该方法可以返回指定元素相对于其他指定元素的index位置,只需将返 ...