Error Boundary实现原理

Error Boundary的定义

Error Boundary是一种组件,或者说是类组件。它需要定义 getDerivedStateFromError 或者 componentDidCatch 生命周期方法。
它自身有三种功能:

  • 捕获渲染期间的错误
  • 打印这些错误
  • 降级展示UI

Error Boundary捕获错误的原理

React的工作流程分为render阶段和commi阶段。所以React需要分别捕获这两个阶段抛出的错误。

在render阶段的入口函数中,renderRootSync和renderRootConcurrent在分别调用workLoopSync和WorkLoopConcurrent方法时都会通过tryCatch语句进行包裹。
这样就能捕获同步执行render阶段和commit阶段抛出的错误了。

捕获render阶段错误的方法叫handleError,捕获commit阶段错误的方法叫captureCommitPhaseError。

不管是handleError还是captureCommitPhaseError,都会从当前发生错误的节点的父节点开始向上遍历,找到最近的Error Boundary。

找到Error Boundary后会做两件事:构建callback 和 执行callback。

构建callback

通过createClassErrorUpdate方法会创建update对象,然后针对

  • 定义getDerivedStateFromError方法,将update.payload属性赋值为匿名函数,在匿名函数中会执行getDerivedStateFromError这个方法。再将update.callback属性复制为一个匿名函数,在函数中会执行logCapturedError方法抛出React的提示信息、
  • 定义componentDidCatch方法,将update.callback属性赋值为匿名函数,在匿名函数中将执行logCapturedError抛出React的提示信息,并将错误信息和栈信息作为参数执行componentDidCatch生命周期函数

如果没有找到Error Boundary,将遍历到根节点,在根节点构建callback。创建update对象,然后赋值update.callback属性为匿名函数,在函数中执行onUncaughtError方法抛出未捕获的错误,和执行logCapturedError抛出React的提示信息。

执行callback

在render阶段的beginWork方法中执行processUpdateQueue将计算这些update,主要是执行保存在update.payload的匿名函数,间接执行getDerivedStateFromError方法将返回值合并到this.state中。

并将构造的回调函数update.callback都保存在fiber.updateQueue.effects数组中。

在commit阶段的layout阶段,执行commitUpdateQueue方法依次执行fiber.updateQueue.effects中保存的callback。在此时将执行上述构造的各个回调函数。

包括:getDerivedStateFromError构造的logCapturedError、componentDidCatch构造的logCapturedError和componentDidCatch,根节点构造的onUncaughtError和logCapturedError。

降级展示UI

在handleError方法中会先调用throwException方法。在throwException方法中对于ClassComponent类型的组件会执行 workInProgress.flags |= ShouldCapture,然后创建update (和构造callback的update逻辑一致),然后将update插入到workInProgress fiber.updateQueue中构成单向环状链表。

执行完throwException方法后会执行completeUnitOfWork方法,在completeUniOfWork方法中会执行unwindWork方法。unwindWork方法会针对包含ShouldCapture的workInProgress fiber节点(即在throwException方法中赋值)再执行 workInProgress.flags = (flags & ~ShouldCapture) | DidCapture(意思就是把ShouldCapture从flags中删除再合并DidCapture)。然后返回 workInProgress。

因为整个 try catch 语句被包裹在do while循环中,所以会再次执行workLoopConcurrent方法再开始执行beginWork方法。首先会根据ClassComponent执行updateClassComponent。调用processUpdateQueue计算update(即getDerivedStateFromError方法拿到返回的结果)得到最新的state(将结果合并到this.state)然后执行finishClassComponent方法执行this.render方法。直到最后执行完commit阶段就会页面上展示降级后的UI。

总结

捕获渲染期间的错误 是通过 handError 方法结合 try catch语句实现的。
打印这些错误 是通过 logCapturedError 方法和 componentDidCatch 生命周期函数实现的。
降级展示UI 是通过getDerivedStateFromError实现的。

posted @ 2024-08-16 17:45  Rocen  阅读(185)  评论(0)    收藏  举报