目录

  • 代码分割
  • React的懒加载
    • import() 原理
    • React.lazy 原理
    • Suspense 原理
  • 参考

1.代码分割

(1)为什么要进行代码分割?

现在前端项目基本都采用打包技术,比如 Webpack,JS逻辑代码打包后会产生一个 bundle.js 文件,而随着我们引用的第三方库越来越多或业务逻辑代码越来越复杂,相应打包好的 bundle.js 文件体积就会越来越大,因为需要先请求加载资源之后,才会渲染页面,这就会严重影响到页面的首屏加载。

而为了解决这样的问题,避免大体积的代码包,我们则可以通过技术手段对代码包进行分割,能够创建多个包并在运行时动态地加载。现在像 Webpack、 Browserify等打包器都支持代码分割技术。

(2)什么时候应该考虑进行代码分割?

这里举一个平时开发中可能会遇到的场景,比如某个体积相对比较大的第三方库或插件(比如JS版的PDF预览库)只在单页应用(SPA)的某一个不是首页的页面使用了,这种情况就可以考虑代码分割,增加首屏的加载速度。

2.React的懒加载

示例代码:

import React, { Suspense } from 'react';const OtherComponent = React.lazy(() => import('./OtherComponent'));function MyComponent() {return (<div><Suspense fallback={<div>Loading...</div>}>        <OtherComponent /></Suspense></div>);
}

如上代码中,通过 import()React.lazySuspense 共同一起实现了 React 的懒加载,也就是我们常说了运行时动态加载,即 OtherComponent 组件文件被拆分打包为一个新的包(bundle)文件,并且只会在 OtherComponent 组件渲染时,才会被下载到本地。

那么上述中的代码拆分以及动态加载究竟是如何实现的呢?让我们来一起探究其原理是怎样的。

import() 原理

import() 函数是由TS39提出的一种动态加载模块的规范实现,其返回是一个 promise。在浏览器宿主环境中一个import()的参考实现如下:

function import(url) {return new Promise((resolve, reject) => {const script = document.createElement("script");const tempGlobal = "__tempModuleLoadingVariable" + Math.random().toString(32).substring(2);script.type = "module";script.textContent = `import * as m from "${url}"; window.${tempGlobal} = m;`;script.onload = () => {resolve(window[tempGlobal]);delete window[tempGlobal];script.remove();};script.onerror = () => {reject(new Error("Failed to load module script with URL " + url));delete window[tempGlobal];script.remove();};document.documentElement.appendChild(script);});
}

当 Webpack 解析到该import()语法时,会自动进行代码分割。

参考React实战视频讲解:进入学习

React.lazy 原理

以下 React 源码基于 16.8.0 版本

React.lazy 的源码实现如下:

export function lazy<T, R>(ctor: () => Thenable<T, R>): LazyComponent<T> {let lazyType = {?typeof: REACT_LAZY_TYPE,_ctor: ctor,// React uses these fields to store the result._status: -1,_result: null,};return lazyType;
}

可以看到其返回了一个 LazyComponent 对象。

而对于 LazyComponent 对象的解析:

...
case LazyComponent: {const elementType = workInProgress.elementType;return mountLazyComponent(current,workInProgress,elementType,updateExpirationTime,renderExpirationTime,);
}
...
function mountLazyComponent(_current,workInProgress,elementType,updateExpirationTime,renderExpirationTime,
) { ...let Component = readLazyComponentType(elementType);...
}
// Pending = 0, Resolved = 1, Rejected = 2
export function readLazyComponentType<T>(lazyComponent: LazyComponent<T>): T {const status = lazyComponent._status;const result = lazyComponent._result;switch (status) {case Resolved: {const Component: T = result;return Component;}case Rejected: {const error: mixed = result;throw error;}case Pending: {const thenable: Thenable<T, mixed> = result;throw thenable;}default: { // lazyComponent 首次被渲染lazyComponent._status = Pending;const ctor = lazyComponent._ctor;const thenable = ctor();thenable.then(moduleObject => {if (lazyComponent._status === Pending) {const defaultExport = moduleObject.default;lazyComponent._status = Resolved;lazyComponent._result = defaultExport;}},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;}}
}

注:如果 readLazyComponentType 函数多次处理同一个 lazyComponent,则可能进入Pending、Rejected等 case 中。

从上述代码中可以看出,对于最初 React.lazy() 所返回的 LazyComponent 对象,其 _status 默认是 -1,所以首次渲染时,会进入 readLazyComponentType 函数中的 default 的逻辑,这里才会真正异步执行 import(url)操作,由于并未等待,随后会检查模块是否 Resolved,如果已经Resolved了(已经加载完毕)则直接返回moduleObject.default(动态加载的模块的默认导出),否则将通过 throw 将 thenable 抛出到上层。

为什么要 throw 它?这就要涉及到 Suspense 的工作原理,我们接着往下分析。

Suspense 原理

由于 React 捕获异常并处理的代码逻辑比较多,这里就不贴源码,感兴趣可以去看 throwException 中的逻辑,其中就包含了如何处理捕获的异常。简单描述一下处理过程,React 捕获到异常之后,会判断异常是不是一个 thenable,如果是则会找到 SuspenseComponent ,如果 thenable 处于 pending 状态,则会将其 children 都渲染成 fallback 的值,一旦 thenable 被 resolve 则 SuspenseComponent 的子组件会重新渲染一次。

为了便于理解,我们也可以用 componentDidCatch 实现一个自己的 Suspense 组件,如下:

class Suspense extends React.Component {state = {promise: null}componentDidCatch(err) {// 判断 err 是否是 thenableif (err !== null && typeof err === 'object' && typeof err.then === 'function') {this.setState({ promise: err }, () => {err.then(() => {this.setState({promise: null})})})}}render() {const { fallback, children } = this.propsconst { promise } = this.statereturn <>{ promise ? fallback : children }</>}
}

小结

至此,我们分析完了 React 的懒加载原理。简单来说,React利用 React.lazyimport()实现了渲染时的动态加载 ,并利用Suspense来处理异步加载资源时页面应该如何显示的问题。

深度剖析React懒加载原理相关推荐

  1. JPA/hibernate懒加载原理分析及JSON格式API反序列化时连环触发懒加载问题的解决

    什么是懒加载 JPA是java持久层的API,也就是java官方提供的一个ORM框架,Spring data jpa是spring基于hibernate开发的一个JPA框架.Spring data j ...

  2. js实现图片懒加载原理(marksheng)

    有时候一个网页会包含很多的图片,例如淘宝京东这些购物网站,商品图片多只之又多,页面图片多,加载的图片就多.服务器压力就会很大.不仅影响渲染速度还会浪费带宽.比如一个1M大小的图片,并发情况下,达到10 ...

  3. vue 图片懒加载和懒加载原理

    在真实图片得到之前,展示懒加载设置的图片1.安装cnpm i vue-lazyload -S2.main.jsimport VueLazyload from 'vue-lazyload'Vue.use ...

  4. mybatis 的懒加载原理

    断断续续的阅读 mybatis 的源码有好几个月了,想把自己了解到的一些东西与大家分享.今天给大家分享一下 mybatis 的懒加载原理. mybatis 的懒加载过程挺复杂的,涉及到的东西有很多,包 ...

  5. js实现图片懒加载原理

    有时候一个网页会包含很多的图片,例如淘宝京东这些购物网站,商品图片多只之又多,页面图片多,加载的图片就多.服务器压力就会很大.不仅影响渲染速度还会浪费带宽.比如一个1M大小的图片,并发情况下,达到10 ...

  6. 路由懒加载原理及使用

    懒加载解决的问题: 避免进入首页就加载全部的前端资源造成用户等待时间过长的问题. 就好比,访问 login 页面,你返回的 js 路由不仅有渲染 login 页面的,还有渲染 production 页 ...

  7. js图片懒加载原理、实现及节流优化

    1.懒加载原理 在图片没有进入可视区域时,先给的src一个默认加载的图片,这样浏览器就不会发送请求了,等到图片进入可视区域再把真实的图片路径data-src给src. 2.具体实现 1. 效果 2. ...

  8. spring bean的懒加载原理

    spring bean的懒加载原理 1 普通的bean的 初始化是在初始化阶段开始执行的,而被lazy-init修饰的bean则是从容器第一次进行context.getbean("" ...

  9. react性能优化-懒加载原理

    编译阶段的优化 开发阶段构建更快 loader的include和exclude属性 {test: /.(j|t)sx?$/,use: [{loader: "thread-loader&quo ...

最新文章

  1. 如何防止apk程序被反编译
  2. Android L 新特性
  3. 计算机文件夹报告范文,2020年计算机实验报告打印(例文).docx
  4. 小甲鱼零基础入门python二十一课课后题_小甲鱼Python第二十一讲课后习题
  5. vue data数据修改_VUE的数据响应式
  6. 直播电商在新商业环境下的价值分析
  7. ActionContext保存客户提交的参数,session会话等
  8. iPhone如何拍摄惊人的照片
  9. NOIP2008pj luoguP1058 立体图 模拟
  10. SQLServer中sp_Who、sp_Who2和sp_WhoIsActive介绍和查看监视运行
  11. 再看健康码和随申码,对比健康码和身份证,想想延展
  12. excel表格公式使用失败,输出只有公式,没显示结果
  13. idou老师教你学Istio12 : Istio 实现流量镜像
  14. 中标麒麟服务器性能怎么样,中标麒麟Linux系统的性能分析及工具(74页)-原创力文档...
  15. 企业信息与网络通信安全 团队成员简历-叶俊
  16. 1秒出图,全球最快的开源Stable Diffusion出炉
  17. java number比较大小_JAVA Number与Math类
  18. RR RC 隔离级别
  19. 毫秒级返回数据,TDengine 在大疆车载智能驾驶云端平台上的应用
  20. CentOS7配置本地yum源 和yum源服务器

热门文章

  1. Ecshop后台拿shell方法总结
  2. sql多表联查,索引
  3. spring 框架技术
  4. 更改计算机磁盘,win10X系统磁盘盘符如何更改 简单快速修改电脑磁盘盘符
  5. LLVM编译器Kaleidoscope(万花筒)入门教学
  6. 苹果se2上市准确时间_苹果手机iPhone屏幕使用时间(ScreenTime)失效不准确的解决办法...
  7. 国内、国外计算机软件类的证书有哪些?
  8. excel中查找绝对值最大的方法
  9. 苹果xr一直在白苹果_苹果watcyouve一直戴错了苹果手表
  10. 我扥等萨拉丁骷髅法师角度看