Build your own React_5 渲染和提交阶段
Posted 一只前端小马甲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Build your own React_5 渲染和提交阶段相关的知识,希望对你有一定的参考价值。
前端工程师的要求越来越高,仅懂得“三大马车”和调用框架API,已经远不能满足岗位的能力要求。因此增强自身的底层能力,了解框架的内部原理非常重要。本系列文章,翻译自Rodrigo Pombo的《Build your own React》一文,同时每篇文章最后,都会加入自己的理解,一方面记录自己初探React框架原理的过程,另一方面也是想与各位大牛多多交流,以出真知。
我们打算从零开始重写一个React框架,在遵循源码架构的基础上,省略了一些优化和非核心功能代码。
假设你阅读过我之前的文章《build your own React》,那篇文章是基于React 16.8版本,当时还不能使用hooks来替代class。
你可以在Didact仓库找到那篇文章和对应的代码。这里还有个相同主题的视频,跟文章的内容有些区别的,但是可以参考观看。
从零开始重写React框架,我们需要遵循以下步骤:
步骤五:渲染和提交阶段
我们现在有另一个问题:每次渲染一个元素,我们都会往DOM中添加一个新节点。还记得前面说过,在完成整个DOM树渲染之前,浏览器可以打断我们的工作。这么来看,用户会看到不完整的UI,显然我们不希望这样。
// requestIdleCallback(workLoop)
function performUnitOfWork(fiber)
// if(!fiber.dom)
// fiber.dom = createDom(fiber)
//
if(fiber.parent)
fiber.parent.dom.appendChild(fiber.dom)
// const elements = fiber.props.children
// let index = 0
// let prevSibling = null
我们删除performUnitOfWork中,修改DOM的代码。
// requestIdleCallback(workLoop)
function performUnitOfWork(fiber)
// if(!fiber.dom)
// fiber.dom = createDom(fiber)
//
// const elements = fiber.props.children
// let index = 0
// let prevSibling = null
取而代之,我们一直追溯纤维树的根节点,并把它称之为正在运行的根节点(work in progress root)或wipRoot。
// function render(element, container)
wipRoot =
// dom: container,
// props:
// children: [element],
//
//
nextUnitOfWork = wipRoot
// let nextUnitOfWork = null
let wipRoot = null
一旦完成了所有工作(我们知道什么时候没有下一个渲染单元),我们把整个纤维树提交给DOM。
function commitRoot()
// TODO add nodes to dom
// function render(element, container)
// wipRoot =
// dom: container,
// props:
// children: [element]
//
//
// nextUnitOfWork = wipRoot
//
// let nextUnitOfWork = null
// let wipRoot = null
function workLoop(deadline)
// let shouldYield = false
// while(nextUnitOfWork && !shouldYield)
// nextUnitOfWork = performUnitOfWork(nextUnitOfWork)
// shouldYield = deadline.timeRemaining() < 1
//
if(!nextUnitOfWork && wipRoot)
commitRoot();
// requestIdleCallback(workLoop)
// requestIdleCallback(workLoop)
提交纤维树的工作在commitRoot函数中完成,在这里我们会递归地将所有的节点添加至DOM中。
function commitRoot()
commitWork(wipRoot.child)
wipRoot = null
function commitWork(fiber)
if(!fiber)
return
const domParent = fiber.parent.dom
domParent.appenChild(fiber.dom)
commitWork(fiber.child)
commitWork(fiber.sibling)
总结
步骤四中,作者提出了纤维的概念,结合步骤三的并发模式,详细介绍了把任务拆成小单元的实现。
步骤五则指出了并发模式存在的问题,每个纤维渲染完成都可以被浏览器打断,用户会看到不完整的UI界面。
实际上作者在前面的章节总共考虑了两种极端情况:
- “囫囵吞枣”思路:递归渲染UI,主线程一直阻塞直到所有渲染任务完成
- “细嚼慢咽”思路:渲染任务被拆成了小单元,任意单元渲染完都能被浏览器中止渲染过程
显然,这两者都不好,程序设计上来说也应当符合“中庸”之道,作者此时提出了改进思路——只有当整个纤维树完成创建节点时,才会整体添加至DOM中。这实际上就是将两种思路结合的结果。
上一篇传送门:Build your own React_4 理解React纤维
下一篇传送门:Build your own React_6 调解器
以上是关于Build your own React_5 渲染和提交阶段的主要内容,如果未能解决你的问题,请参考以下文章
Build your own React_4 理解React纤维