likes
comments
collection
share

React18:可能不是你想象中的时间切片

作者站长头像
站长
· 阅读数 26

背景

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

React18:可能不是你想象中的时间切片

什么是优先级调度?

要说时间切片优先级调度必然也是要说到,为什么呢,因为时间切片这项feature仅在并发调度时启用,那么何为并发调度并发调度你可以理解为手动控制调度的优先级,可以让你认为的低优先级任务延迟执行,这里我们只简单提下在React18中如何开启使用,只需使用新的Concurrent API将调度标记为低优先级即可(如下示例使用的useTransition)

这种情况时间切片有效吗?

如下code,存在一个耗时组件BusyComprender阶段会花费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时间一致,无法获得丝滑的渲染体验~,这里认为时间切片失效

效果如下: React18:可能不是你想象中的时间切片

那么时间切片适用于什么场景呢?

首先我们思考下为什么上面的例子里时间切片失效了,耗时组件的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渲染的空闲时间

效果如下: React18:可能不是你想象中的时间切片

这里就可以得出结论,时间切片更适用于fiber树巨大但不存在单个fiber render 耗时巨大的情况

感谢大家的阅读~