fre 1.5 发布了……新的时间切片实现

你们好,真的很久不见………………由于如今暑假实习太忙,基本没太有时间写本身的东西了react

悲剧……因此一直想要重构 fre,拖到如今才实现,下面看看此次更新主要作了什么事情:git

新的时间切片

在以前的版本中,时间切片主要是借助 requestIdleCallback 完成的,它存在一些问题,好比兼容性不好(主要是小程序不支持),好比不可控(没法控制切片的帧率,丢帧也不知道),总之这个 API 确定要搞掉的github

react 的时间切片,是基于 requestAnimation + 优先级 来调度实现的,理想状态下,fre 也应该这么作算法

const FPS = 1000 / 60

function workLoop (startTime = 0) {
  if (startTime && performance.now() - startTime > FPS) {
    requestAnimationFrame(workLoop)
  } else {
    const nextTime = performance.now()
    nextWork = performWork(nextWork)
    if (nextWork) {
      workLoop(nextTime)
    } else {
      options.commitWork
        ? options.commitWork(pendingCommit)
        : commitWork(pendingCommit)
    }
  }
}
复制代码

如上图,fre 一样基于 requestAnimationFrame 实现了一个超级小的调度,它的原理是这样的:小程序

  • 若是一个任务它可以在一帧内完成,那能够同步的进入下一个任务
  • 若是有十个任务,他们均可以在一帧内完成,那么同步的过程也不会卡(60fps)
  • 若是有任务没法在一帧内完成,那么它的下一个任务须要开启下一帧
  • 下一帧的任务是下一个切片,由同步变成异步,保证复杂任务不干扰下一个任务

如上,短短的十行代码,切片的同时,拥有较好的兼容性,并且切片的过程还可控,之后随时能够将更新队列按照优先级进行排序缓存

diff 算法优化

如今的 fre 能够说是孤立无援,react 的 diff 看不懂,其余框架的 diff 不适用于 fiber框架

本次优化主要是针对两个 case:异步

function App () {
  const [arr, setArr] = useState(['A', 'B','C','D'])
  return (
    <div> <ul> {arr.map(item => ( <A key={item} val={item}/> ))} </ul> <button onClick={() => setArr(['B','A','D','C'])}>+</button> </div> ) } function A(props){ return <div>{props.val}</div> } 复制代码

如上,这个是其中一个 case ,将 key 加到组件上,此时的 key 应该转移到组件内部的函数

根元素(div)上,这也是为何咱们要求组件必须有一个元素包裹的缘由oop

若是不这么作,key 加到组件上,等于没加

{
  isShow && <A /> isShow ? <A /> : null isShow ? <A /> : <B /> } 复制代码

这是另外一个 case,以前一直想优化这种状况,发现很是困难,后来发现我是错的

这种 case 是很难优化的,因此移除掉这部分的优化,尺寸也有史以来的第一次缩减到 1.7kb

位置的变化是很困难的,只有加 key 的状况能够作到位置变化

fix bug

终于搞懂了 useMemo / useCallback / useEffect 的关系,以前的实现有点点问题,新版本处理掉了

useMemo 是根据第二个参数判断是否须要从新计算并返回一个缓存过的 值

useCallback是根据第二个参数判断是否须要从新生成并返回一个缓存过的 函数引用

useEffect 是根据第二个参数判断是否须要执行这个函数

总结 综上所述,fre 新版本逻辑更加精巧了,我也按照约定,搞了时间切片和调度,至此,fre 算得上麻雀虽小,五脏俱全了

github 地址:github.com/132yse/fre

孤立无援,欢迎老铁加入进来,一块儿研究 fiber 下的算法~

相关文章
相关标签/搜索