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纤维

Build your own React_4 理解React纤维

Build your own React_0 总述

Build your own React_0 总述

Build your own React_8 Hooks

Build your own React_7 函数组件