建立你自己的react(四)---并发模式

88 阅读2分钟

前言

如果是接触过react 15版本的同学们可能会有感触,随着项目越来越大,当你操作input的时候,会一卡一卡的,这是怎么回事呢?react后来是如何解决这个问题的呢?

并发模式

但是,,,,,在添加更多的代码之前,我们需要一个重构

有一个关于递归回调的问题

function render(element, container) {
  element.props.children.forEach(child =>
    render(child, dom)
  )
}

一旦我们开始渲染,我们将不能停下来,直到我们已经完成整个element树的渲染。如果这个element树很大,它或许会阻塞这个主线程很长时间。如果这个浏览器需要做一些高优先级的事情,比如处理用户的input输入或者保持一个动画的平滑,它将不得不等待直到这个渲染结束。

所以我们将将把这个工作拆分成更小的单元,在完成每一个单元任务以后,我们可以使浏览器打断渲染,如果有更高优先级的事情需要做。

let nextUnitOfWork = nullfunction workLoop(deadline) {
  let shouldYield = false
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(
      nextUnitOfWork
    )
    shouldYield = deadline.timeRemaining() < 1
  }
  requestIdleCallback(workLoop)
}
​
requestIdleCallback(workLoop)
​
function performUnitOfWork(nextUnitOfWork) {
  // TODO
}

我们使用requestIdleCallback去做一个循环。你可以考虑requestIdleCallback作为一个setTimeout, 而不是当它运行的时候我们需要告诉他,这个浏览器将运行这个回调,当浏览器主线程空闲的时候。

function workLoop(deadline) {
  requestIdleCallback(workLoop)
}
​
requestIdleCallback(workLoop)

但是React不再使用requestIdleCallback, 现在他使用的是调度程序包。但是在这里我们使用requestIdleCallback, 因为他们在概念上是相同的。

requestIdleCallback也给了我们一个deadline参数. 我们可以用它来检测我们还有多少时间直到这个浏览器需要再次拿回控制权。

function workLoop(deadline) {
  let shouldYield = false
  while (nextUnitOfWork && !shouldYield) {
    // 如果剩余时间小于1, 那么把控制权交出去
    shouldYield = deadline.timeRemaining() < 1
  }

}

截止2019年11月,并发模式还不稳定。这个稳定的循环版本看起来更多像这样

while (nextUnitOfWork) {    
  nextUnitOfWork = performUnitOfWork(   
    nextUnitOfWork  
  ) 
}

开始使用循环的时候,我们需要设置第一个工作单元,然后写一个不仅仅执行工作也需要返回下一个工作单元的performUnitOfWork方法

let nextUnitOfWork = nullfunction workLoop(deadline) {
    nextUnitOfWork = performUnitOfWork(
      nextUnitOfWork
    )
}

function performUnitOfWork(nextUnitOfWork) {
  // TODO
}

今天完整代码如下

let nextUnitOfWork = nullfunction workLoop(deadline) {
  let shouldYield = false
  while (nextUnitOfWork && !shouldYield) {
    nextUnitOfWork = performUnitOfWork(
      nextUnitOfWork
    )
    shouldYield = deadline.timeRemaining() < 1
  }
  requestIdleCallback(workLoop)
}
​
requestIdleCallback(workLoop)
​
function performUnitOfWork(nextUnitOfWork) {
  // TODO
}

参考