站在React的角度理解React

Jun 21, 2023

前言

本文基于建造属于你的react 这篇文章整理出来React的简单实现过程和一些核心概念,具体的细节和实现过程请参考原文。
React渲染过程中有两个重要的对象 ReactElement 对象和 fiber 对象
  1. ReactElement 对象
所有采用jsx语法书写的节点, 都会被编译器转换, 最终会以React.createElement(...)的方式, 创建出来一个与之对应的ReactElement对象。
ReactElement对象可以理解为jsx语法的翻译。
  1. fiber 对象
fiber对象是通过ReactElement对象进行创建的, 多个fiber对象构成了一棵fiber树, fiber树 是构造DOM树的数据模型, fiber树的任何改动, 最后都可以体现到DOM上。
为什么要引入fiber树呢,是因为如果直接用ReactElement对象渲染页面的话,只能一次递归处理完,这样浏览器呈现卡死的状态,但是使用fiber树渲染页面可以随时中断,将控制权交回浏览器,让位给高优先级的任务,浏览器空闲后再恢复渲染。

ReactElement 对象

jsx 转换为原生 js 需要通过一些诸如babel的编译工具。编译的过程把所有元素标签部分所有内容转换为 React.createElement(...)函数,给函数传递元素标签名,标签上面的属性(prop),以及标签的子节点(children)。
例如如下的jsx节点:
 
const element = ( <div id="id"> <div className="className"> <h3 style="color: red"> <span>hello</span> </h3> </div> <header> <h2>World</h2> </header> </div> )
最终会被React.createElement(...)转化成如下的一个对象返回:
 
{ "type": "div", "props": { "id": "id", "children": [ { "type": "div", "props": { "className": "className", "children": [ { "type": "h3", "props": { "style": "color: red", "children": [ { "type": "span", "props": { "children": [ { "type": "TEXT_ELEMENT", "props": { "nodeValue": "hello", "children": [] } } ] } } ] } } ] } }, { "type": "header", "props": { "children": [ { "type": "h2", "props": { "children": [ { "type": "TEXT_ELEMENT", "props": { "nodeValue": "World", "children": [] } } ] } } ] } } ] } }

fiber 对象

当 dom tree 很大的情况下,直接用ReactElement 对象渲染页面,页面上会是长时间的卡住状态,无法进行用户输入等交互操作。
可分为以下步骤解决上述问题:
  1. 允许中断渲染工作,如果有优先级更高的工作插入,则暂时中断浏览器渲染,待完成该工作后,恢复浏览器渲染;
  1. 将渲染工作进行分解,分解成一个个小单元;
步骤1可以使用浏览器的requestIdleCallbackAPI解决允许中断渲染工作的问题,步骤2就需要引入feiber对象处理了。
假设需要渲染上述ReactElement 对象,生成的 fiber树如图:
notion image
fiber 树的目标是非常容易找到下一个单元工作,这也是为什么每一个 fiber 节点都有指向第一个节点和相邻节点以及父节点的链接。当我们完成在 fiber 上面的工作后,fiber 拥有 child 属性可以直接指向下一个需要进行工作的 fiber 节点。
当 fiber节点没有child也没兄弟节点时,我们去他们的叔叔(父节点的兄弟节点)节点,如果fiber的父节点也没有兄弟节点,我们继续往上找父节点的兄弟节点直到到根节点。当我们到根节点的时候,也意味着在这一次render我们完成了所有的工作。

具体的渲染流程

了解了两个最重要的对象和requestIdleCallbackAPI后,React内部的渲染逻辑可以简单抽象为这样:
notion image
按照这个流程其实React第一次渲染时并不快,因为要额外生成fiber树,但是依靠fiber树这个数据结构,React更新是很快的,因为React保留了上次渲染完的fiber树。

State的简单处理

可以将State的信息挂在到当前的fiber对象中,这样每次SetState时,重新触发一次workloop就可以了。

后记

这里只是针对建造属于你的react 这篇文章做了简单的总结,具体的实现细节还是建议先把建造属于你的react 这篇文章过一遍。
 

参考

Copyright © 2025 later

logo