Suspense,一个容易被忽略的性能优化方式

前言

Suspense技术起初是为了加载异步组件,但逐渐应用到了全部异步过程当中。其中,异步请求是Suspense最主要的使用场景。在不远的未来(React17/Vue3.1),基于Suspense的开发模式将成为主流,同时用户体验也会随着Suspense的使用获得优化。html


Suspense的使用方式

Vue的使用方式

codesandbox.io/s/bold-cdn-…
node

React的使用方式

codesandbox.io/s/kind-swar…
react


Suspense原理

Suspense做为一个通用的技术,React和Vue在实现的思路上基本一致,核心原理有两方面:通讯和渲染。promise

通讯是指:Suspense与嵌入其内部的组件之间的通讯,即Suspense如何获取组件的异步状态性能优化

渲染是指:Suspense如何控制嵌入其内部的组件的渲染bash

Suspense如何获取组件的异步状态

结论:经过“隐式”的获取组件异步状态所关联的promise。网络

表面上看来,不论是Vue仍是React,咱们都没有给Suspense传任何promise相关的props,那么Suspense究竟是如何拿到的呢?咱们固化的思惟容易认为:props是通往组件的惟一入口。架构

实际上,Suspense做为一个框架内置的组件,它有“充分的自由”去获取任何运行时中存在的变量。并发

  • 对于Vue来讲,若是在setup中"return"了一个promise,那么Vue就能拿到这个promise,并自动传递给离这个组件最近的那个Suspense。框架

  • 对于React来讲,若是在render中"throw"了一个promise,那么React就能捕获到这个promise,并自动传递给离这个组件最近的那个Suspense。

这样,Suspense就能获取到组件的异步状态了。

Suspense如何控制嵌入其内部的组件的渲染

结论:预渲染嵌入的组件到一个隐藏的dom容器中,并在异步状态resolve以后把预渲染的组件移动到真实的dom容器中。

Suspense总会预渲染组件到一个隐藏的容器,以后它会根据所关联的promise的状态进行抉择:

  1. 若是promise处于pending状态,那么就会渲染fallback组件到真实的dom容器中

  2. 若是promise到达resolve状态,那么就会直接把预渲染好的组件移动到真实的dom容器中,并清除fallback组件

  3. 若是promise到达reject状态,那么框架会直接抛出reject的错误,后面能够被ErrorBoundary接收

第一阶段用户体验优化

在组件中加载异步数据是业务场景中最多见的需求,组件的状态会根据异步数据的状态不一样而变化,一般是:loading状态 -> 真实内容。

使用传统的v-if/v-else,或者jsx中的三元表达式的形式,去渲染不一样状态下对应的组件内容,实际会有性能问题:

// template
<Loading v-if="loading">loading...</Loading>
<Content v-else>真实内容</Content>

// jsx
(
	loading ? <Loading>loading</Loading> : <Content>真实内容</Content>
)复制代码

在切换渲染两种不一样的element时,都伴随着组件的销毁与重建。而若是使用Suspense,真实内容和fallback这二者会同时存在,真实内容不会随着fallback出现而销毁,也不会随着fallback的消失而重建,相反真实内容只是在更新。

这样,用户体验就获得了第一阶段的优化,由于页面的渲染性能明显提高。


第二阶段用户体验优化(仅React)

渲染性能已经获得明显的提高,那么咱们还能优化什么呢?React并发模式带来了新的思路:干掉loading状态。

React并发模式是React Fiber架构的应用,在React Fiber架构下,组件树的更新以组件为粒度被异步化,组件的更新能够被中断,从而不会阻塞主线程。

更多内容请看:reactjs.org/docs/concur…

实际环境中,用户的设备性能、网络速度各有不一样,有的人性能好网速快,有的人性能差网速慢,结果就致使这种状况:

设备条件

使用体验

缘由

设备条件已经这么好了,加载数据毫秒级别,可是仍是要闪一下loading状态

设备条件很差,加载数据秒级别,有一个loading状态会很舒服

在React并发模式中结合使用Suspense,便可解决上述问题,这里有一个demo:

codesandbox.io/s/elated-ca…

  1. 点击Refresh会更新列表

  2. 更新列表的接口耗时随机在0-1s之间

  3. 若是接口返回的时间在0.5s以内,则不会显示loading状态

  4. 若是接口返回的时间在0.5s以外,则会显示loading状态


FAQ

Wait,我不用React并发模式也能实现第二阶段用户体验优化

是的,咱们彻底能够自行实现这个功能,无非是加一个定时器,只有超过某个时间后,才会显示loading。可是,这样咱们就享受不到Suspense预渲染带来的性能提高了。

Vue用户体验就必定不如React吗?

虽然Vue在Suspense上没法贡献更多性能优化,可是Vue会从另一个方向:预分析vnode,根据类型不一样给予相应的patchFlag,并根据flag的不一样采起最高效的更新方式,带来显著的渲染性能提高。

相关文章
相关标签/搜索