当前位置: 首页 > Web前端 > vue.js

为什么Hook没有ErrorBoundary?

时间:2023-04-01 02:05:20 vue.js

大家好,我是Kason。在很多充分使用Hooks开发的团队中,唯一使用ClassComponent的场景就是使用ClassComponent创建ErrorBoundary。可以说,如果Hooks有以下两种生命周期函数替代,ClassComponent完全可以放弃:getDerivedStateFromErrorcomponentDidCatch那为什么还没有标准的Hook呢?今天我们就从上面两个生命周期函数的实现原理和移植到Hook的代价来谈谈这个问题。欢迎加入人类优质前端框架群,一起飞翔ErrorBoundary实现原理ErrorBoundary可以在后代组件中捕获React工作流中的错误。React工作流是指:render阶段,即组件渲染和Diff算法发生的阶段。Commit阶段,即渲染DOM并执行componentDidMount/Update的阶段。这就是为什么发生在事件回调中的错误不能被ErrorBoundary捕获的原因——事件回调不属于React工作流。如何捕获错误render阶段的整体执行流程如下:do{try{//render阶段的具体执行流程workLoop();休息;}catch(thrownValue){handleError(root,thrownValue);}}而(真);可以发现,如果在render阶段出现错误,就会被捕获,并执行handleError方法。同理,commit阶段的整体执行流程如下:将被捕获??并执行captureCommitPhaseError方法。getDerivedStateFromError原理捕获到的错误如何处理?我们知道ClassComponent中this.setState的第一个参数,除了可以接收新的状态外,还可以接收改变状态的函数作为参数://Thiscanbelikethis.setState(this.state.num+1)//也可以这样,this.setState(num=>num+1)getDerivedStateFromError的实现利用了this.setState改变状态的特性。捕获错误后,即:对于render阶段,执行handleError后,对于commit阶段,执行captureCommitPhaseError后,会在ErrorBoundary对应的组件中触发类似如下的更新:this.setState(getDerivedStateFromError。bind(null,error))这就是为什么getDerivedStateFromError需要开发者返回新的状态——本质上,他触发了一个新的更新。componentDidCatch的原理我们再来看另一个ErrorBoundary相关的生命周期函数——componentDidCatch。ClassComponent中this.setState的第二个参数可以接收一个回调函数作为参数:this.setState(newState,()=>{//...callback})当触发更新渲染到页面时,回调会扳机。这就是componentDidCatch的实现原理。当捕获到错误时,会在对应的ErrorBoundary组件中触发类似如下的更新:this.setState(this.state,componentDidCatch.bind(this,error))React运行过程中,都已经被React自己捕获,然后交给ErrorBoundary处理。如果未定义ErrorBoundary,则需要重新抛出这些捕获的错误,从而产生未捕获到错误的感觉。这个步骤在哪里执行?与this.setState类似,ReactDOM.render(element,container[,callback])的第三个参数也可以接收回调函数。如果开发人员没有定义ErrorBoundary,那么React最终会在ReactDOM.render回调中抛出错误。可以发现,ClassComponent中ErrorBoundary的实现完全依赖于ClassComponent已有的特性。但是Hooks本身并没有类似this.setState的回调特性,所以实现起来会比较复杂。在Hooks中实现ErrorBoundary除了上面提到的障碍外,FunctionComponent和ClassComponent在源码层面的运行过程细节也存在差异,同样难以复制和实现。如果一定要实现,在最大程度复用现有基础设施的指导下,useErrorBoundary(ErrorBoundary在Hooks中的实现)应该类似如下使用:functionErrorBoundary({children}:{children:ReactNode}){const[errorMsg,updateError]=useState<错误|空>(空);useErrorBoundary((e:Error)=>{//捕获到错误,触发更新updateError(e);})return(

{errorMsg?'Error:'+errorMsg.toString():children}
)}其中useErrorBoundary的触发方式与useEffect类似:useErrorBoundary((e:Error)=>{//...})//类似于useEffect(()=>{//...})作者模仿ClassComponent中ErrorBoundary和useEffect的实现原理,实现了原生的Hooks——useErrorBoundary。有兴趣的朋友可以在useErrorBoundary的在线例子中体验一下效果。总结ClassComponent中ErrorBoundary的实现使用了this.setState的回调函数特性,需要额外的开发成本才能在Hooks中完全实现相同的功能。笔者猜测,这也是没有提供对应的nativeHooks的原因之一。