自己造轮子系列——React框架并发模型探讨

近来工作忙了一阵,加上自己精力不足,终于造轮子系列搁置了许久。废话不多说,接着上次的轮子,继续造。

写在前面

React框架具体的实现细节十分庞杂,这里就挑几个最具代表性的特性——JSXFunction ComponentHooks,当然每一个特性的实现细节也十分繁琐,我们的目标是实现核心功能理解原理就好。另外对于React最新的Fibers树结构并发模型,也简单探讨下。

总结下来,具体要实现的轮子:

  1. React.createElement 函数——核心功能,解析JSX;
  2. ReactDOM.render 函数——核心功能;
  3. Concurrent Mode并发模型——探讨;
  4. Fibers Tree——探讨;
  5. Function Component;
  6. Hooks;
  7. 对比源码

正文

前一篇中,我们一起实现了React.createElementReactDOM.render两个函数,今天我们来探讨一下第三个主题:Concurrent Mode并发模型。

为什么这一节我们要讲并发模型呢?其实是因为上一章中,我们在处理DOM树时使用了递归,而递归会导致一个问题:就是一旦我们开始了就不能停止,这将会浪费不少性能和资源,当DOM树很大时,可能会阻塞主线程,从而导致页面卡顿等不好的用户体验。

function render(element, container) {
    const dom =
        element.type == "TEXT_ELEMENT"
        ? document.createTextNode("")
        : document.createElement(element.type)

    const isProperty = key => key !== "children"
    Object.keys(element.props)
        .filter(isProperty)
        .forEach(name => {
            dom[name] = element.props[name]
        })
    // 递归整个DOM树
    element.props.children.forEach(child =>render(child, dom))
    container.appendChild(dom)
}



那怎么解决这个问题呢?有一个不错的办法是:将整个任务分割成若干个小任务,每个小任务完结时,浏览器可以根据需要拦截下个小任务,从而可以执行优先级更高的比如用户输入之类的任务,在浏览器执行完优先级更高的任务之后,可以接着执行下个小任务,这样就很大程度上减少了卡顿发生的概率。

那么具体的代码要怎么实现呢?

let nextUnitOfWork = null

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

requestIdleCallback(workLoop)

function performUnitOfWork(nextUnitOfWork) {
    // TODO
}



这里我们使用requestIdleCallback作为队列,可以把它想象成一个setTimeOut,但是不用设置执行时间,requestIdleCallback会在主线程空闲时执行。

需要说明的是,React已经不再使用requestIdleCallback,现在使用的是调度程序包,但是对于我们现在这个轮子来讲,作用一样,区别并不大。

前面的例子中,requestIdleCallback也能传递一个deadline参数,通过这个时间,我们可以知道浏览器再次控制之前我们还有多少时间。

写在后面

上面的requestIdleCallback,精简一下就是这样:

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

如果要开始使用队列,我们需要设置第一个任务单元,然后编写一个performUnitOfWork函数,它不仅执行当前任务,还返回下一个任务。

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYny7VeP' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片