关于前端:Error-Boundaries是这么实现的还挺巧妙

27次阅读

共计 2879 个字符,预计需要花费 8 分钟才能阅读完成。

大家好,我卡颂。

本文会解说 ReactError Boundaries的残缺实现逻辑。

一张图概括:

这里简略解说下 React 工作流程,后文有用。分为三步:

  1. 触发 更新
  2. render 阶段:计算 更新 会造成的 副作用
  3. commit 阶段:在宿主环境执行 副作用

副作用 有很多,比方:

  • 插入 DOM 节点
  • 执行 useEffect 回调

好了,让咱们进入主题。

欢送退出人类高质量前端框架群,带飞

什么是 Error Boundaries

React提供了两个与 错误处理 相干的API

  • getDerivedStateFromError:静态方法,当谬误产生后提供一个机会渲染fallback UI
  • componentDidCatch:组件实例办法,当谬误产生后提供一个机会记录错误信息

应用了这两个 APIClassComponent通常被称为Error Boundaries(谬误边界)。

Error Boundaries子孙组件 中产生的所有 React 工作流程内 的谬误都会被 Error Boundaries 捕捉。

通过开篇的介绍能够晓得,React 工作流程 指:

  • render 阶段
  • commit 阶段

思考如下代码:

class ErrorBoundary extends Component {componentDidCatch(e) {console.warn(“产生谬误”, e);
  }
  render() {return <div>{this.props.children}</div>;
  }
}

const App = () => (
    <ErrorBoundary>
    <A><B/></A>
    <C/>
    <ErrorBoundary>
)

ABC作为 ErrorBoundary 的子孙组件,当产生 React 工作流程内 的谬误,都会被 ErrorBoundary 中的 componentDidCatch 办法捕捉。

步骤 1: 捕捉谬误

首先来看 工作流程中的谬误都是何时被捕捉的

render阶段的外围代码如下,产生的谬误会被 handleError 解决:

do {
  try {
    // 对于并发更新则是 workLoopConcurrent
workLoopSync();
    break;
  } catch (thrownValue) {handleError(root, thrownValue);
  }
} while (true);

commit阶段蕴含很多工作,比方:

  • componentDidMount/Update执行
  • 绑定 / 解绑ref
  • useEffect/useLayoutEffect callbackdestroy 执行

这些工作会以如下模式执行,产生的谬误被 captureCommitPhaseError 解决:

try {// …执行某项工作} catch (error) {captureCommitPhaseError(fiber, fiber.return, error);
}

步骤 2: 结构 callback

能够发现,即便没有 Error Boundaries 工作流程 中的谬误曾经被 React 捕捉了。而正确的逻辑应该是:

  • 如果存在Error Boundaries,执行对应API
  • 抛出 React 的提示信息
  • 如果不存在 Error Boundaries,抛出 未捕捉的谬误

所以,不论是 handleError 还是captureCommitPhaseError,都会从产生谬误的节点的父节点开始,逐层向上遍历,寻找最近的Error Boundaries

一旦找到,就会结构:

  • 用于 执行 Error Boundaries APIcallback
  • 用于 抛出 React 提示信息 callback

  // ... 为了可读性,逻辑有删减
function createClassErrorUpdate() {if (typeof getDerivedStateFromError === 'function') {
// 用于执行 getDerivedStateFromError 的 callback
    update.payload = () => {return getDerivedStateFromError(error);
};
// 用于抛出 React 提示信息的 callback
    update.callback = () => {logCapturedError(fiber, errorInfo);
    };
  }
  if (inst !== null && typeof inst.componentDidCatch === 'function') {
// 用于执行 componentDidCatch 的 callback
    update.callback = function callback() {this.componentDidCatch(error);
    };
  }
  return update;
}

如果没有找到Error Boundaries,持续向上遍历直到根节点。

此时会结构:

  • 用于 抛出未捕捉谬误 callback
  • 用于 抛出 React 提示信息 callback
// ... 为了可读性,逻辑有删减
funffction createRootErrorUpdate() {
  // 用于抛出“未捕捉的谬误”及“React 的提示信息”的 callback
  update.callback = () => {onUncaughtError(error);
    logCapturedError(fiber, errorInfo);
  };
  return update;
}

执行 callback

结构好的 callback 在什么时候执行呢?

React 中有两个 执行用户自定义 callbackAPI

  • 对于 ClassComponentthis.setState(newState, callback)newStatecallback 参数都能传递 Function 作为callback

所以,对于Error Boundaries,相当于被动触发了一次更新:

this.setState(() => {// 用于执行 getDerivedStateFromError 的 callback}, () => {
  // 用于执行 componentDidCatch 的 callback
  //  以及 用于抛出 React 提示信息的 callback
})
  • 对于根节点,执行 ReactDOM.render(element, container, callback)callback参数能传递 Function 作为callback

所以,对于 没有 Error Boundaries的状况,相当于被动执行了如下函数:

ReactDOM.render(element, container, () => {// 用于抛出“未捕捉的谬误”及“React 的提示信息”的 callback})

所以,Error Boundaries的实现能够看作是:React利用已有 API 实现的新性能。

总结

常常有人问:为什么 Hooks 没有Error Boundaries

能够看到,Error Boundaries的实现借助了 this.setState 能够传递 callback 的个性,useState临时无奈齐全对标。

最初,给你留个作业,在官网文档介绍了 4 种状况的谬误不会被 Error Boundaries 捕捉。

利用本文常识,你能剖析下他们为什么不会被捕捉么?

正文完
 0