Build your own React_3 并发模式
Posted 一只前端小马甲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Build your own React_3 并发模式相关的知识,希望对你有一定的参考价值。
前端工程师的要求越来越高,仅懂得“三大马车”和调用框架API,已经远不能满足岗位的能力要求。因此增强自身的底层能力,了解框架的内部原理非常重要。本系列文章,翻译自Rodrigo Pombo的《Build your own React》一文,同时每篇文章最后,都会加入自己的理解,一方面记录自己初探React框架原理的过程,另一方面也是想与各位大牛多多交流,以出真知。
我们打算从零开始重写一个React框架,在遵循源码架构的基础上,省略了一些优化和非核心功能代码。
假设你阅读过我之前的文章《build your own React》,那篇文章是基于React 16.8版本,当时还不能使用hooks来替代class。
你可以在Didact仓库找到那篇文章和对应的代码。这里还有个相同主题的视频,跟文章的内容有些区别的,但是可以参考观看。
从零开始重写React框架,我们需要遵循以下步骤:
步骤三:并发模式
在新增功能代码之前,我们需要对之前的代码重构,因为原来的代码有个问题。
function render(element, container){
// const dom = element.type = "TEXT_ELEMENT"
// ? document.createTextNode("")
// : document.createElement(element.type)
// const isProperty = key => key !== "children"
// Object.keys(elemnet.props)
// .filter(isProperty)
// .forEach(name => {
// dom[name] = element.props[name]
// })
element.props.children.forEach(child=>
render(child, dom)
)
// container.appendChild(dom)
}
问题就是:一旦开始渲染就停不下来,除非整个元素树渲染完成。如果元素树太大,渲染过程会阻塞主进程太多时间。而如果此时浏览器有更高优先级的任务,例如响应用户输入或是保证开场动画的流畅度,均需要等待所有元素树渲染完成。
因此我们的思路是:将渲染任务分成小的单元,每完成一个单元,如果此时有更高优先级的任务,浏览器就会暂停渲染过程。
let nextUnitOfWork = null
function workLoop(deadline){
let sholdYield = false
while(nextUnitOfWork && !shouldYield){
nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
shouldYield = deadline.timeRemaining() < 1
}
requestIdleCallback(workLoop)
}
requestIdleCallback(workLoop)
function performUnitOfWork(nextUnitOfWork){
// TODO
}
我们使用requestIdleCallback来执行循环。可以把requestIdleCallback类似于setTimeout函数,但区别在于我们无需告诉它什么时候运行,浏览器在主线程闲置的时候会自动执行传入的回调参数。
值得一提的是:React已经不再使用requestIdleCallback,它使用的是scheduler包,针对这个功能,两者本质上是一样的。
// let UnitOfWork = null
function workLoop(deadline){
// let shouldYield = false
// while(nextUnitOfWork && !shouldYield){
// nextUnitOfWork = preformUnitOfWork(nextUnitOfWork)
// shouldYield = deadline.timeRemaining() < 1
// }
requestIdleCallback(workLoop)
}
requestIdleCallback(workLoop)
requestIdleCallback同时会提供一个表示dealine的参数,我们可以利用它判断浏览器多久需要收回控制权。
function workLoop(deadline){
let shouldYield = false
while(nextUnitOfWork && !shouldYield){
// nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
shouldYield = deadline.timeRemaining() < 1
}
// requestIdleCallback(workLoop)
}
截止到2019年11月份,并发模式在React中不再属于稳定版,渲染循环任务的稳定版代码类似于下:
while (nextUnitOfWork){
nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
}
为了开始整个渲染工作,我们需要手动设置第一个单元渲染任务,然后通过编写performUnitOfWork函数来执行后续的单元渲染任务,函数的作用不仅是执行当前单元渲染任务,还能返回下个单元渲染任务。
let nextUnitOfWork = null
function workLoop(deadline){
// let shouldYield = false
// while(nextUnitOfWork && !shouldYield){
nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
// shouldYield = deadline.timeRemaining() < 1
//}
//reuestIdleCallback(workLoop)
}
// requestIdleCallback(workLoop)
function performUnitOfWork(nextUnitOfWork){
// TODO
}
总结
步骤二中,作者详细介绍了每个React元素是如何被转化为实际的DOM节点。
步骤三则指出了步骤二中递归渲染节点的问题:无法中止渲染,造成的后果是用户体验很差。同时提出了解决方案,将整棵DOM树的渲染拆分成小的单元任务,每完成单元渲染,如果此时有更高优先级任务,浏览器会暂停渲染。至此,我们开始深入了解React的渲染细节,大任务拆小体现了”分而治之“的思想。
上一篇传送门:Build your own React_2 render函数
下一篇传送门:Build your own React_4 理解React纤维
以上是关于Build your own React_3 并发模式的主要内容,如果未能解决你的问题,请参考以下文章
Build your own React_4 理解React纤维