大厂的前端白屏监控解决方案,别再说你不会

本文详细解析了React组件渲染过程,从jsx语法转换到FiberNode的构建,以及正常渲染和错误边界处理的详细流程。重点介绍了FiberNode在虚拟DOM中的作用以及错误边界如何捕获和重试组件渲染。
摘要由CSDN通过智能技术生成

{ children }

</>

);

const Child = () => 

I’m child

const a = ReactDOM.render(

,

document.getElementById(‘root’)

);

准备

首先浏览器是不认识我们的 jsx 语法的,所以我们通过 babel 编译大概能得到下面的代码:

var App = function App(_ref2) {

var children = _ref2.children;

return React.createElement(“p”, null, “hello”), children);

};

var Child = function Child() {

return React.createElement(“p”, null, “I’m child”);

};

ReactDOM.render(React.createElement(App, null, React.createElement(Child, null)), document.getElementById(‘root’));

babel 插件将所有的 jsx 都转成了 createElement 方法,执行它会得到一个描述对象 ReactElement 大概长这样子:

{

$$typeof: Symbol(react.element),

key: null,

props: {}, // createElement 第二个参数 注意 children 也在这里,children 也会是一个 ReactElement 或 数组

type: ‘h1’ // createElement 的第一个参数,可能是原生的节点字符串,也可能是一个组件对象(Function、Class…)

}

所有的节点包括原生的 <a></a><p></p> 都会创建一个 FiberNode ,他的结构大概长这样:

FiberNode = {

elementType: null, // 传入 createElement 的第一个参数

key: null,

type: HostRoot, // 节点类型(根节点、函数组件、类组件等等)

return: null, // 父 FiberNode

child: null, // 第一个子 FiberNode

sibling: null, // 下一个兄弟 FiberNode

flag: null, // 状态标记

}

你可以把它理解为 Virtual Dom 只不过多了许多调度的东西。最开始我们会为根节点创建一个 FiberNodeRoot 如果有且仅有一个 ReactDOM.render 那么他就是唯一的根,当前有且仅有一个 FiberNode 树。

我只保留了一些渲染过程中重要的字段,其他还有很多用于调度、判断的字段我这边就不放出来了,有兴趣自行了解

render

现在我们要开始渲染页面,是我们刚才的例子,执行 ReactDOM.render 。这里我们有个全局 workInProgress 对象标志当前处理的 FiberNode

  1. 首先我们为根节点初始化一个 FiberNodeRoot ,他的结构就如上面所示,并将 workInProgress= FiberNodeRoot

  2. 接下来我们执行 ReactDOM.render 方法的第一个参数,我们得到一个 ReactElement :

ReactElement = {

$$typeof: Symbol(react.element),

key: null,

props: {

children: {

$$typeof: Symbol(react.element),

key: null,

props: {},

ref: null,

type: ƒ Child(),

}

}

ref: null,

type: f App()

}

该结构描述了 <App><Child /></App>

  1. 我们为 ReactElement 生成一个 FiberNode 并把 return 指向父 FiberNode ,最开始是我们的根节点,并将 workInProgress = FiberNode

{

elementType: f App(), // type 就是 App 函数

key: null,

type: FunctionComponent, // 函数组件类型

return: FiberNodeRoot, // 我们的根节点

child: null,

sibling: null,

flags: null

}

  1. 只要workInProgress 存在我们就要处理其指向的 FiberNode 。节点类型有很多,处理方法也不太一样,不过整体流程是相同的,我们以当前函数式组件为例子,直接执行 App(props) 方法,这里有两种情况

  2. 该组件 return 一个单一节点,也就是返回一个 ReactElement 对象,重复 3 - 4 的步骤。并将当前 节点的 child 指向子节点 CurrentFiberNode.child = ChildFiberNode 并将子节点的 return 指向当前节点 ChildFiberNode.return = CurrentFiberNode

  3. 该组件 return 多个节点(数组或者 Fragment ),此时我们会得到一个 ChildiFberNode 的数组。我们循环他,每一个节点执行 3 - 4 步骤。将当前节点的 child 指向第一个子节点 CurrentFiberNode.child = ChildFiberNodeList[0] ,同时每个子节点的 sibling 指向其下一个子节点(如果有) ChildFiberNode[i].sibling = ChildFiberNode[i + 1] ,每个子节点的 return 都指向当前节点 ChildFiberNode[i].return = CurrentFiberNode

如果无异常每个节点都会被标记为待布局 FiberNode.flags = Placement

  1. 重复步骤直到处理完全部节点 workInProgress 为空。

最终我们能大概得到这样一个 FiberNode 树:

FiberNodeRoot = {

elementType: null,

type: HostRoot,

return: null,

child: FiberNode,

sibling: null,

flags: Placement, // 待布局状态

}

FiberNode {

elementType: f App(),

type: FunctionComponent,

return: FiberNodeRoot,

child: FiberNode

,

sibling: null,

flags: Placement // 待布局状态

}

FiberNode

 {

elementType: ‘p’,

type: HostComponent,

return: FiberNode,

sibling: FiberNode,

child: null,

flags: Placement // 待布局状态

}

FiberNode {

elementType: f Child(),

type: FunctionComponent,

return: FiberNode,

child: null,

flags: Placement // 待布局状态

}

提交阶段

提交阶段简单来讲就是拿着这棵树进行深度优先遍历 child => sibling,放置 DOM 节点并调用生命周期。

那么整个正常的渲染流程简单来讲就是这样。接下来看看异常处理

错误边界流程

刚刚我们了解了正常的流程现在我们制造一些错误并捕获他:

const App = ({ children }) => (

<>

hello

{ children }

</>

);

const Child = () => 

I’m child {a.a}

const a = ReactDOM.render(

,

document.getElementById(‘root’)

);

执行步骤 4 的函数体是包裹在 try...catch 内的如果捕获到了异常则会走异常的流程:

do {

try {

workLoopSync(); // 上述 步骤 4

break;

} catch (thrownValue) {

handleError(root, thrownValue);

}

} while (true);

执行步骤 4 时我们调用 Child 方法由于我们加了个不存在的表达式 {a.a} 此时会抛出异常进入我们的 handleError 流程此时我们处理的目标是 FiberNode<Child> ,我们来看看 handleError :

function handleError(root, thrownValue): void {

let erroredWork = workInProgress; // 当前处理的 FiberNode 也就是异常的 节点

throwException(

root, // 我们的根 FiberNode

erroredWork.return, // 父节点

erroredWork,

thrownValue, // 异常内容

);

completeUnitOfWork(erroredWork);

}

function throwException(

root: FiberRoot,

returnFiber: Fiber,

sourceFiber: Fiber,

value: mixed,

) {

// The source fiber did not complete.

sourceFiber.flags |= Incomplete;

let workInProgress = returnFiber;

do {

switch (workInProgress.tag) {

case HostRoot: {

workInProgress.flags |= ShouldCapture;

return;

}

case ClassComponent:

// Capture and retry

const ctor = workInProgress.type;

const instance = workInProgress.stateNode;

if (

(workInProgress.flags & DidCapture) === NoFlags &&

(typeof ctor.getDerivedStateFromError === ‘function’ ||

(instance !== null &&

typeof instance.componentDidCatch === ‘function’ &&

!isAlreadyFailedLegacyErrorBoundary(instance)))

) {

workInProgress.flags |= ShouldCapture;

return;

}

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
img

OPPO等大厂,18年进入阿里一直到现在。**

深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-HKppibiJ-1710923843144)]
[外链图片转存中…(img-pd9XGDXV-1710923843145)]
[外链图片转存中…(img-uWP60De9-1710923843146)]
[外链图片转存中…(img-xEZsmypf-1710923843146)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
[外链图片转存中…(img-VQurRUC7-1710923843147)]

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值