跳到主要内容

认识

2023年12月28日
柏拉文
越努力,越幸运

一、认识


React.lazy 内部模拟一个 promiseA 规范场景。完全可以理解 React.lazyPromise 模拟了一个请求数据的过程,但是请求的结果不是数据,而是一个动态的组件。下一次渲染就直接渲染这个组件,所以是 React.lazy 利用 Suspense 接收 Promise ,执行 Promise ,然后再渲染这个特性做到动态加载的,代码如下:

react/src/ReactLazy.js
function lazy(ctor){
return {
$$typeof: REACT_LAZY_TYPE,
_payload:{
_status: -1, //初始化状态
_result: ctor,
},
_init:function(payload){
if(payload._status===-1){ /* 第一次执行会走这里 */
const ctor = payload._result;
const thenable = ctor();
payload._status = Pending;
payload._result = thenable;
thenable.then((moduleObject)=>{
const defaultExport = moduleObject.default;
resolved._status = Resolved; // 1 成功状态
resolved._result = defaultExport;/* defaultExport 为我们动态加载的组件本身 */
})
}
if(payload._status === Resolved){ // 成功状态
return payload._result;
}
else { //第一次会抛出Promise异常给Suspense
throw payload._result;
}
}
}
}

整个流程是这样的,React.lazy 包裹的组件会标记 REACT_LAZY_TYPE 类型的 element,在调和阶段会变成 LazyComponent 类型的 fiberReactLazyComponent 会有单独的处理逻辑:

  • 第一次渲染首先会执行 init 方法,里面会执行 lazy 的第一个函数,得到一个 Promise,绑定 Promise.then 成功回调,回调里得到将要渲染组件 defaultExport ,这里要注意的是,如上面的函数当第二个 if 判断的时候,因为此时状态不是 Resolved ,所以会走 else ,抛出异常 Promise,抛出异常会让当前渲染终止。

  • 这个异常 Promise 会被 Suspense 捕获到,Suspense 会处理 PromisePromise 执行成功回调得到 defaultExport(将想要渲染组件),然后 Susponse 发起第二次渲染,第二次 init 方法已经是 Resolved 成功状态,那么直接返回 result 也就是真正渲染的组件。这时候就可以正常渲染组件了

二、细节