react源码解读之ReactLazy.js

「这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战」。

Wakeable,Thenable傻傻分不清楚

import type {Wakeable, Thenable} from 'shared/ReactTypes';
export interface Wakeable {
  then(onFulfill: () => mixed, onReject: () => mixed): void | Wakeable;
}
export interface Thenable<+R> {
  then<U>(
    onFulfill: (value: R) => void | Thenable<U> | U,
    onReject: (error: mixed) => void | Thenable<U> | U,
  ): void | Thenable<U>;
}
import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';

REACT_LAZY_TYPE = symbolFor('react.lazy');
复制代码
  • Wakeable,可唤醒的。这样一个接口规定实现它的类,必须实现一个then方法,这个then方法的返回值类型为void(空)或者Wakeable。它接收两个函数参数,成功时的回调和失败时的回调。
  • Thenable接口的定义与Wakeable的定义类似,只是函数参数的入参不一样,ThenableonFulfill方法的返回值可以为Thenable,也就是说可以无限地往下then下去。
  • REACT_LAZY_TYPE标识符标识了react的懒加载类型,读完源码你会知道react懒加载类型有何作用。

如果这里不太明白,建议继续往下看,山穷水复疑无路,柳暗花明又一村,不要在这颗树上吊死了。

问题Wakeable与Thenable有何不一样?

Thenable的状态值

const Uninitialized = -1;
const Pending = 0;
const Resolved = 1;
const Rejected = 2;
复制代码
  • Uninitialized,表示未初始化,值为-1。
  • Pending,表示正在加载中,值为0。
  • Resolved,表示已经加载完成并且加载成功,值为1。
  • Rejected,表示已经加载完成但是加载失败,值为2。

真的值得吗?

payload表示有效载荷,关键信息 假设Thenable方法会返回一个未知量,那么这个未知量代表什么呢? 请阅读下面的代码:

type UninitializedPayload<T> = {
  _status: -1,
  _result: () => Thenable<{default: T, ...}>,
};

type PendingPayload = {
  _status: 0,
  _result: Wakeable,
};

type ResolvedPayload<T> = {
  _status: 1,
  _result: {default: T},
};

type RejectedPayload = {
  _status: 2,
  _result: mixed,
};
复制代码

根据状态值,将有效载荷分为4种类型。

  • UninitializedPayload,未初始化的有效载荷,_result属性为一个函数返回一个Thenable。
  • PendingPayload,加载中的有效载荷,_result属性为一个Wakeable.
  • ResolvedPayload,已完成的有效载荷,_result属性为一个对象,默认值为泛型T对应的数据类型。
  • RejectedPayload,已失败的有效载荷,—result属性为mixed,表示任意类型。

可以看到,在Resolved阶段,返回的是任意数据类型(mixed),这个mixed又由Wakeable返回,也就是加载中有效载荷的_result属性执行后返回。未初始化的有效载荷的_result属性调用后,返回一个Thenable,这个Thenable会返回一个Wakeable,也就是将会返回实际数据的一个函数。ResolvedPayload就是我们实际所需要的数据。

爆炸,就简简单单的一个异步调用返回数据的功能,被设计成了四层。这真的值得吗?

ctor是个什么der


export function lazy(ctor) {
  const payload = {
    _status: Uninitialized,
    _result: ctor,
  };

  const lazyType = {
    $$typeof: REACT_LAZY_TYPE,
    _payload: payload,
    _init: lazyInitializer,
  };
  return lazyType;
}
复制代码

最后,react导出了一个lazy函数,万事大吉。

ctor是个什么der?

首先,我们看看React.lazy这个api的使用。

const LazyComponent = React.lazy(() => import('../componets/LazyComponent));
复制代码

可以看到,ctor是一个函数,这个函数返回一个Thenable的异步调用。

function resolveLazy(lazyType) {
  const payload = lazyType._payload;
  const init = lazyType._init;
  return init(payload);
}
function lazyInitializer(payload) {
  if (payload._status === Uninitialized) {
    const ctor = payload._result;
    const thenable = ctor();
    thenable.then()
    ...
    }
复制代码

resolveLazy方法会调用lazyInitializer的_init方法,最开始,会进入未初始化的状态判断。 调用ctor得到一个thenable的对象。在异步调用过程中,payload的状态将是Pending。 调用结束之后会返回moduleObject.default,返回这个组件渲染所必须的payload。

异步路由不够,第三方包也要异步。

在一个项目的迭代过程中,随着模块的越来越多,webpack打包的第三方包将会越来越多,这样导致系统首次加载将会越来越慢。虽然有了异步路由的加持,但是在main.js文件里加入过多的文件,项目大到一定程度,仍将导致主入口文件的体积过大。React的lazy方法值得我们学习,去处理项目第三方包过多的问题。

感谢阅读,欢迎动动你的小手,给个赞吧。

猜你喜欢

转载自juejin.im/post/7035996414874222623