React18:可能不是你想象中的时间切片
背景
随着React18的正式发布,Concurrent API已经稳定,目前 npm 默认版本已经指向18.2.0,也有着不小的下载量,相信未来会有越来越多的项目用上React18,这里我们主要来了解下用于解决CPU瓶颈的时间切片(time slicing)

什么是优先级调度?
要说时间切片,优先级调度必然也是要说到,为什么呢,因为时间切片这项feature仅在并发调度时启用,那么何为并发调度?
并发调度你可以理解为手动控制调度的优先级,可以让你认为的低优先级任务延迟执行,这里我们只简单提下在React18中如何开启使用,只需使用新的Concurrent API将调度标记为低优先级即可(如下示例使用的useTransition)
这种情况时间切片有效吗?
如下code,存在一个耗时组件BusyComp,render阶段会花费1000ms,我们对其使用memo进行了优化,同时使用startTransition标记了setText为低优先级调度,后续仅有setCount(高优先级调度)执行时,其不会重复render。
通过这 2 个不同优先级的调度,我们就可以判断出时间切片是否有效
import React, {
useState,
useTransition,
} from 'react'
const BusyComp: React.FC<{
text: number
}> = React.memo(({ text }) => {
const current = performance.now()
while (performance.now() - current < 1000) {
// something
}
return <div>I'm busy...{text}</div>
})
export default function TransitionComp() {
const [isPending, startTransition] =
useTransition()
const [count, setCount] = useState('')
const [text, setText] = useState(0)
return (
<div>
<input
value={count}
onChange={({ target: { value } }) => {
setCount(value)
startTransition(() => {
setText((c) => c + 1)
})
}}
/>
<div>
{isPending && <div>加载中</div>}
<BusyComp text={text}></BusyComp>
</div>
</div>
)
}
这里给出答案:在连续进行输入操作时,明显能感受到高优先级的count渲染卡顿,卡顿时间与耗时组件render时间一致,无法获得丝滑的渲染体验~,这里认为时间切片失效
效果如下:

那么时间切片适用于什么场景呢?
首先我们思考下为什么上面的例子里时间切片失效了,耗时组件的render过程中,是执行同步的script,这个执行过程中React也是没有办法中断的,script执行和UI渲染都在主线程上,也就阻塞了UI渲染,出现明显卡顿,时间切片失效
我们修改下BusyComp部分代码,如下,这时候时间切片又是否有效呢?
let num = 0
const BusyComp: React.FC<{
text: number
}> = React.memo(({ text }) => {
const current = performance.now()
while (performance.now() - current < 10) {
// something
}
if (num < 100) {
num++
return <BusyComp text={text} />
}
num = 0
return <div>I'm busy...{text}</div>
})
结果是获得了如德芙般丝滑的体验~,setCount这个高优先级调度渲染完全不受影响,而低优先级的setText调度则被打断(低优先级任务不会被饿死,不同优先级的任务有不同的timeout时间,timeout时间到了就会立即执行,可以尝试连续输入几秒,会发现低优先级的text也会断断续续地渲染出来),render阶段被切片,在浏览器的每一帧中都让出了UI渲染的空闲时间
效果如下:

这里就可以得出结论,时间切片更适用于fiber树巨大但不存在单个fiber render 耗时巨大的情况
感谢大家的阅读~
转载自:https://juejin.cn/post/7119014764017090568