react.lazy 路由懒加载_React lazy/Suspense使用及源码解析

React v16.6.0已经发布快一年了,为保障项目迭代发布,没有及时更新react版本,最近由于开启了新项目,于是使用新的react版本进行了项目开发。项目工程如何搭建,如何满足兼容性要求,如何规范化等等这里不作为介绍重点,这里想说一下react的lazy,suspense,这块在react官网作为code-splitting重点说明过,可见其出现的意义。

官网写的比较详细,总结起来就是,如果你项目中使用webpack或browserify进行打包,随着工程项目的增长和大量三方库的引入,会使你打包后的文件逐渐变大,用户加载文件时,会花大量时间去加载他们并不关心的内容,而此时,懒加载React.lazy的概念就应运而生。

注意:官网提示React.lazy并不适合SSR

这里介绍基于路由的懒加载,也是比较常用的方式,React.lazy只要一句话就能实现,如下:

const OtherComponent = React.lazy(async () => import('./OtherComponent'));

lazy中的函数返回Promise对象,引入了导出React Component的文件,并且官方提示为了有过度效果,还提供了Suspense组件,而且如果不引入的话还会报错,如下:

<Suspense fallback={<div>Loading...</div>}><OtherComponent />
</Suspense>

以上都是官网示例,在项目实际使用中,还没有单独对功能组件进行懒加载,可以依据业务租组件的复杂度决定是否使用懒加载,个人觉得路由的懒加载是有必要的。使用中我们用高阶组件进行Suspense的封装:

const WithLazyLoad = (WrappedComponent: React.ComponentType<any>) =>class HOC extends React.Component {private displayName = `HOC(${getDisplayName(WrappedComponent)})`;public render() {console.log(this.displayName)return (<React.Suspense fallback={<div>Loading...</div>} ><WrappedComponent {...this.props} /></React.Suspense>  )}};

在App.tsx中对路由进行定义,这里假设有三个路由地址:

const About = React.lazy(() => import('./components/About/About'));
const Hello = React.lazy(() => import('./components/Hello/Hello'));
const Home = React.lazy(() => import('./components/Home/Home'));class App extends React.Component {public render() {return (<BrowserRouter><Switch><Route path="/" exact={true} component={WithLazyLoad(Hello)}  /><Route path="/home" exact={true} component={WithLazyLoad(Home)} /><Route path="/about" exact={true} component={WithLazyLoad(About)} /></Switch></BrowserRouter>);}
}

以上两步,就完成了基本功能对实现,我们来看下效果

v2-3f502daa07913add3276195b60fb3cca_b.png
使用Lazy

使用lazy后会根据路由打包成多个chunk文件,进行按需加载。我们打印懒加载的组件信息,返回的是个对象,示意如下:

v2-33c2606efaec6bad6535ff66ad31da8a_b.jpg
React.lazy(() =&amp;gt; import(&amp;#39;./components/Home/Home&amp;#39;))返回对象
主要属性说明:
$$typeof:对象类型,包括Symbol(react.lazy)、Symbol(react.element)、Symbol(react.portal)等等,在react源码中有定义
_ctor:懒加载异步函数,返回Promise对象,即 async () => import('./Home')
_result:存储懒加载异步函数执行的结果,可能值为error、moduleObject.default(即ƒ Home())
_status:当前状态,初始值(-1)、Pending(0)、Resolved(1)、Rejected(2)

查看react源码,在react-dom.js文件下的beginWork函数中,可以看到LazyComponent的加载方式其实是调用了mountLazyComponent函数,

switch (workInProgress.tag) {// ...case LazyComponent:{var _elementType = workInProgress.elementType;return mountLazyComponent(current$$1, workInProgress, _elementType, updateExpirationTime, renderExpirationTime);}// ...
}

查看mountLazyComponent函数,最重要的地方是,下面会分步解析:

// 解析lazy component
var Component = readLazyComponentType(elementType);
// Store the unwrapped component in the type.
workInProgress.type = Component;
// 获取Component类型,可能值ClassComponent、FunctionComponent、ForwardRef、MemoComponent、IndeterminateComponent
var resolvedTag = workInProgress.tag = resolveLazyComponentTag(Component);
// 初始化props
var resolvedProps = resolveDefaultProps(Component, props);

首先看readLazyComponentType函数,其参数elementType为上面打印出的对象,返回懒加载的组件,下面列出了关键代码,_thenable执行ctor()异步函数,拿到import的组件函数即f home(),拿到后暂存于workInProgress.type:

function readLazyComponentType(lazyComponent) {var status = lazyComponent._status;var result = lazyComponent._result;switch (status) {// ...default:{lazyComponent._status = Pending;var ctor = lazyComponent._ctor;var _thenable = ctor();_thenable.then(function (moduleObject) {if (lazyComponent._status === Pending) {var defaultExport = moduleObject.default;{if (defaultExport === undefined) {warning$1(false, 'lazy: Expected the result of a dynamic import() call. ' + 'Instead received: %snnYour code should look like: n  ' + "const MyComponent = lazy(() => import('./MyComponent'))", moduleObject);}}lazyComponent._status = Resolved;lazyComponent._result = defaultExport;}}, function (error) {if (lazyComponent._status === Pending) {lazyComponent._status = Rejected;lazyComponent._result = error;}});// Handle synchronous thenables.switch (lazyComponent._status) {case Resolved:return lazyComponent._result;case Rejected:throw lazyComponent._result;}lazyComponent._result = _thenable;throw _thenable;}}
}

v2-5cb0174e36850cc0ba9c4b47c3e098a1_b.jpg
正常返回的lazyComponent._result

随后执行resolveLazyComponentTag函数,入参为readLazyComponentType拿到的结果Component,由于我们的返回的是f home(),所以直接用shouldConstruct判断Component的原型上是否有isReactComponent,如果存在则为class组件,否则为函数组件,代码如下:

function resolveLazyComponentTag(Component) {if (typeof Component === 'function') {return shouldConstruct(Component) ? ClassComponent : FunctionComponent;} else if (Component !== undefined && Component !== null) {var $$typeof = Component.$$typeof;if ($$typeof === REACT_FORWARD_REF_TYPE) {return ForwardRef;}if ($$typeof === REACT_MEMO_TYPE) {return MemoComponent;}}return IndeterminateComponent;
}

之后执行resolveDefaultProps,初始化默认的props

function resolveDefaultProps(Component, baseProps) {if (Component && Component.defaultProps) {// Resolve default props. Taken from ReactElementvar props = _assign({}, baseProps);var defaultProps = Component.defaultProps;for (var propName in defaultProps) {if (props[propName] === undefined) {props[propName] = defaultProps[propName];}}return props;}return baseProps;
}

执行完上面的方法,懒加载的前期工作就差不多完成了,下面根据resolvedTag进行组件刷新,我们这里是ClassComponent,所以重点看这块的更新方法updateClassComponent,下面我们逐段分析该方法

switch (resolvedTag) {// ...case ClassComponent:{child = updateClassComponent(null, workInProgress, Component, resolvedProps, renderExpirationTime);break;}// ...}

updateClassComponent方法首先做了propTypes的校验(如果在组件中设置了的话),注意无法在CreateElement中验证lazy组件的属性,只能在updateClassComponent中进行验证。

{if (workInProgress.type !== workInProgress.elementType) {var innerPropTypes = Component.propTypes;if (innerPropTypes) {checkPropTypes(innerPropTypes, nextProps, // Resolved props'prop', getComponentName(Component), getCurrentFiberStackInDev);}}}

然后检查是否有context,如果有的话则设置Provider,并监听变化,随后执行实例化,最后执行finishClassComponent方法,进行Component的render,即CreateElement,渲染到dom上

 var hasContext = void 0;if (isContextProvider(Component)) {hasContext = true;pushContextProvider(workInProgress);} else {hasContext = false;}prepareToReadContext(workInProgress, renderExpirationTime);// ...constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
// ...var nextUnitOfWork = finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime);

Suspense组件的渲染方式类似,也是用updateSuspenseComponent,只不过里面有nextDidTimeout标志,决定是渲染fallback还是其子组件。

上面就是关于React.lazy的一些想要分享和记录的一些内容,如果存在错误的理解或更好的理解方式,希望多多交流

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/394726.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言数据结构系统化,C语言数据结构+数据库+操作系统

http://cv.qiaobutang.com/post/55c419b20cf2009bd4607795第二部分是专业相关的C &#xff0c;数据库&#xff0c;操作系统&#xff0c;数据结构。http://c.biancheng.net/cpp/u/shuju/数据(Data)是信息的载体&#xff0c;它能够被计算机识别、存储和加工处理。它是计算机程序加…

c语言判断一个序列是不是另一个的子序列

1 #include <stdio.h>2 #include <string.h>//添加字符串头文件3 4 int Subsequence(char s[], char t[]) 5 {6 int m,n,i,j;7 n strlen(s); //n表示序列S的长度8 m strlen(t); //m表示序列T的长度9 i0; 10 j0; 11 if (m>…

linux中python如何调用matlab的数据_特征锦囊:如何在Python中处理不平衡数据

今日锦囊特征锦囊&#xff1a;如何在Python中处理不平衡数据? Index1、到底什么是不平衡数据2、处理不平衡数据的理论方法3、Python里有什么包可以处理不平衡样本4、Python中具体如何处理失衡样本印象中很久之前有位朋友说要我写一篇如何处理不平衡数据的文章&#xff0c;整理…

Hadoop不适合哪些场景 哪些场景适合?

Hadoop设计的目的主要包括下面几个方面&#xff0c;也就是所谓的适用场景&#xff1a; 1&#xff1a;超大文件 可以是几百M&#xff0c;几百T这个级别的文件。 2&#xff1a;流式数据访问 Hadoop适用于一次写入&#xff0c;多次读取的场景&#xff0c;也就是数据复制进去之后&a…

阿里Android p6准备,项目经历准备篇——如何准备阿里巴巴P6/P7前端面试

项目经历准备篇——如何准备阿里巴巴P6/P7前端面试在上次的校招文章之后&#xff0c;有很多同学问有没有社招相关的东西可以写一篇&#xff0c;现在它来了。比起校招&#xff0c;社招更加看重项目经历项目经历反应的思考。本文针对的是想进入阿里的P6/P7同学&#xff0c;着重讲…

for in for of区别_Python 第4课:for…in循环黄金搭档之range()函数

乐学趣学Py● 04&#xff1a;for…in循环黄金搭档之range()函数●Python趣味小百科Python中的绘图模块为什么叫Turtle海龟&#xff0c;而不是cat ,dog,bird呢&#xff1f;原来Python引用了麻省理工大学教授开发的logo海龟制图语言,能通过绘图直观地教大家学习编程。实践是最好的…

永恒python图片_python 数据词云展示实例(3)- 背景图设置

记录wordcloud库背景图的设置及样板 之前介绍了wordcloud的基本使用wordcloud的基本使用&#xff0c;本文记录一下如何设置背景图。 样图 背景图tim.jpg 生成样图dream.png 样板 from PIL import Image,ImageSequence image Image.open(tim.jpg)#打开背景图 graph np.array(im…

企业如何杜绝云端数据泄密?

一直以来&#xff0c;云计算所倡导的就是&#xff1a;“我们可以做得更好&#xff0c;更便宜”。云计算带给企业诸多利好&#xff0c;但实施云计算必然会加剧信息泄露风险。当企业打算把所有数据传输云端的时候&#xff0c;首先要考虑的就是数据保护的问题。 使用云服务&#x…

3dmark for android,Android版3DMark首测

听到这个消息估计很多像我一样看腻了3dmark11画面的跑分党及玩家们有些许小失望。 每年的12月影驰都会联合NVIDIA举办盛大的嘉年华&#xff0c;今年在武汉举办的影驰2012电子竞技嘉年华的合作伙伴名单上出现了futumark的身影。Futuremark中国区负责人影驰在当天的平板体验区也提…

android 申请usb权限,USB 权限申请流程

USB android授权方式权限的控制分三块&#xff1a;1:USB host端有个线程循环检测系统是否USB设备插拔&#xff0c;如果有就找到申请权限的APP并调用起来2:APP运行后主动申请权限&#xff0c;也就是requestPermission()接口3:APP运行后直接打开USB设备&#xff0c;这时候USB hos…

qt for android 图片可拉伸,qt实现九宫格布局,图片拉伸

在实现qt播放时&#xff0c;调用的mplayer,由于采用的是自定义绘图&#xff0c;用的是setAttribute(Qt::WA_TranslucentBackground);结果不能正常在上面显示播放画面&#xff0c;在默认皮肤下是没有问题的&#xff0c;决定用九宫格图片拉伸方式(效果如图)附件图片&#xff1a;文…

python不是内部文件_已安装python,但是出现‘python’不是内部或外部命令,也不是可运行的程序或批处理文件。...

解决方法&#xff1a; 1.打开python shell查看你的python安装路径&#xff08;黄色标注&#xff09; >>> import sys >>> sys.path [, C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python\\Python37\\Lib\\idlelib, C:\\Users\\Administrator\\App…

使用canvas绘制时钟

使用canvas绘制时钟 什么使canvas呢&#xff1f;HTML5 <canvas> 元素用于图形的绘制&#xff0c;通过脚本 (通常是JavaScript)来完成。<canvas> 标签只是图形容器&#xff0c;所以我们必须使用脚本来绘制图形。通过它可以绘制路径,盒、圆、字符以及添加图像等等。 …

c++分治法求最大最小值实现_最优化计算与matlab实现(12)——非线性最小二乘优化问题——G-N法...

参考资料《精通MATLAB最优化计算&#xff08;第二版&#xff09;》编程工具Matlab 2019a目录石中居士&#xff1a;最优化计算与Matlab实现——目录​zhuanlan.zhihu.com非线性最小二乘优化问题非线性最小二乘优化也叫无约束极小平方和函数问题&#xff0c;它是如下无约束极小问…

win7 IIS7环境下部署PHP 7.0

最近在本机电脑win7 II7环境下部署PHP 7.0遇到一些问题&#xff0c;将之记录下来 简要步骤如下&#xff1a; 1、到php官网下载php&#xff0c;由于是IIS环境要下载非线程安全的版本&#xff0c;我下载的是7.0.13 2、解压到本地文件目录下 3、通过控制台进入到php文件目录&#…

《Oracle高性能自动化运维》一一3.3 Redo产生场景

3.3 Redo产生场景我们知道&#xff0c;Oracle Redo是以条目&#xff08;Redo Entries/Records&#xff09;的形式记录数据库的所有更改操作&#xff08;OP&#xff09;。更改操作主要包括&#xff1a;数据库物理文件更改&#xff1a;主要指的是数据库物理文件的增减等操作&…

智能算法(GA、DBO等)求解零空闲流水车间调度问题(NIFSP)

先做一个声明&#xff1a;文章是由我的个人公众号中的推送直接复制粘贴而来&#xff0c;因此对智能优化算法感兴趣的朋友&#xff0c;可关注我的个人公众号&#xff1a;启发式算法讨论。我会不定期在公众号里分享不同的智能优化算法&#xff0c;经典的&#xff0c;或者是近几年…

android 简单实现圆角,Android 实现圆角图片的简单实例

Android 实现圆角图片的简单实例实现效果图&#xff1a;本来想在网上找个圆角的例子看一看&#xff0c;不尽人意啊&#xff0c;基本都是官方的Demo的那张原理图&#xff0c;稍后会贴出。于是自己自定义了个View&#xff0c;实现图片的圆角以及圆形效果。效果图&#xff1a;Andr…

zookeeper介绍及集群的搭建(利用虚拟机)

ZooKeeper ​   ZooKeeper是一个分布式的&#xff0c;开放源码&#xff08;apache&#xff09;的分布式应用程序协调服务&#xff0c;是Google的Chubby一个开源的实现&#xff0c;是Hadoop和Hbase、dubbox、kafka的重要组件。它主要用来解决分布式集群中应用系统的一致性问题…

标准库类型String

定义和初始化string对象 初始化string对象方式 string s1 默认初始化&#xff0c;s1是一个空串 string s2(s1) s2是s1的副本 string s2 s1 等价于s2(s1), s2是s1的副本 string s3("value") s3是字面值"value"的副本&#xff0c;除了字面值最后的那个…