按需渲染优化
在很多业务场景中,小程序渲染的页面的高度是超过一屏的。但在首次进入页面时,只需渲染出可视范围的内容即可。当页面首次渲染完毕后,再继续异步渲染剩下的页面内容。
如何按需渲染优化
我们以长列表应用场景为例,说明如何按需渲染:
首先,获取当前可使用窗口高度,计算需要展示的列表数目。可以通过getSystemInfoSync
获取当前的视口高度,假设每行列表的高度固定。计算该视口下可展示的列表数目。
其次,渲染上一步计算出的数目的列表。首次渲染时,仅展示首屏可见的列表,在setData
的回调中,渲染剩余的数据。
// 1. 渲染首屏数据
this.setData({
list: list.slice(0, listNum)
}, () => {
// 2. 在回调中渲染其他的数据
this.setData({list});
})
未优化
优化后
以上是在极端测试条件下的结果,在实际使用中效果可能相对不明显。
最佳实践
我们以一个宝宝知道的问答页作为具体优化的案例介绍。在实际操作中,建议开发者可以通过访问占比分析,以访问量较大的页面作为进行性能优化的重点对象。
问题分析
宝宝知道问答页需要展现的内容较多。按显示顺序从上到下,整个页面的功能点依次为:
- 直播信息横条
- 问题区
- 回答区
- 广告组件区
- 为你推荐信息流
经过分析,我们发现了两个比较明显的问题:
- 页面整体布局比较复杂,加载完数据之后的首次渲染,会一次提交问题区、回答区、广告区三个部分的渲染任务,由于这三个区域涉及的内容量比较大,基本都会超过一屏,甚至两屏以上;
- 各自区域内部,也会包含一些图片视频内容,比如
video
组件、image
组件,这些内容的优先级不如文本。但相对于文本,渲染它们更耗时。
下图为优化前的首屏渲染过程。
优化方案
为了解决上述问题,让用户快速看到最关键的内容,我们按照按需渲染的思路进行优化。另外对于优先级非常低的内容,可以考虑改成由用户某种行为(比如滑动页面)触发加载和渲染。
优化后的问答页渲染示意图,其中绿色部分代表该模块内部进行了部分渲染(只渲染文字,video
组件延迟到第三步再渲染)、蓝色部分代表完全渲染、红色部分表示即将进行异步渲染、白色部分表示未渲染。
以下是优化方案的代码简单示例,请开发者根据自身业务进行借鉴:
- SWAN
- JS
<view>
<view class="question">
<text>{{questionContent}}</text>
</view>
<view class="answer">
<!-- video 组件先使用相同大小的view进行占位,待首屏加载完后渲染 -->
<video s-if="videoShow"></video>
<view s-else style="height:300px;width:400px;"></view>
<text>{{answerContent}}</text>
</view>
<!-- 广告部分在可视区之外,延迟渲染 -->
<view s-if="adShow" class="ad">
<ad></ad>
</view>
<!-- 信息流部分在可视区之外,延迟渲染 -->
<view s-if="feedShow" class="feedList">
<view s-for="item in feedData">
{{item}}
</view>
</view>
</view>
// 仅展现了分段渲染的思路,可根据自身业务架构进行改写
Page({
data: {
// 问题区域数据
questionContent: '',
// 回答区域数据
answerContent: '',
// 信息流部分数据
feedData: [],
// video部分数据
src: '',
// 初始时video组件以及可视区之外的广告、信息流部分,通过 s-if 控制暂不渲染
videoShow: false,
adShow: false,
feedShow: false,
},
// 在 onInit 生命周期中发起网络请求获取页面数据
onInit() {
const page = this;
swan.request({
url: 'xxx',
method: 'GET',
success: res => {
page.setData({
questionContent: res.data.questionContent,
answerContent: res.data.answerContent
}, () => {
// 首屏关键信息渲染完成后,再延迟渲染其他不重要的内容
page.setData({
videoShow: true,
adShow: true,
feedShow: true,
feedData: res.data.feedData,
src: res.data.src
});
});
}
});
}
});
优化后的问答页渲染逻辑,整体上被拆分为四个阶段:
- 核心内容快速渲染阶段。该阶段为
FMP
主要检测的数据渲染时长,所以在这个阶段,我们需要让页面的内容和元素,足够装满一屏。 - 核心内容补全渲染阶段。该阶段将核心内容中存在的耗时内容,比如图片、视频以及原生组件等内容渲染上(视频
video
等原生组件相比普通组件,渲染更加耗时,不适宜放在第一阶段直接渲染,图片image
如果条件允许,也尽量不放在第一屏)。 - 后续内容渲染阶段。该阶段将本次接口返回的需要渲染的数据全部上屏。
- 其他非主要异步数据渲染阶段(图例中的直播信息横条)。
优化成果
该优化版于 2020 年 8 月 4 日上午 11 点左右全量上线,在百度 App 中逐步放量。 FMP 指标在 8 月 5 日和 6 日两天快速下降,7 日逐步稳定。总计优化 FMP 指标 540ms。
有关宝宝知道更详细的性能优化实践,请点击链接跳转至开发者社区查看。