在 onInit 请求首屏主数据
基础库 3.160.12 及以上版本开始支持
Page.onInit
。
大部分小程序,需要发起至少一次网络请求并调用setData
,才能完成整个页面的最终渲染。如果能优化该环节,页面的渲染时间将会大大缩短。
回顾一下我们在性能优化的原理和手段中所介绍的小程序启动流程,可以发现无论是把主数据请求放在App.onLaunch
里还是Page.onLoad
里,都会存在一些难以解决问题:
- 如果在
App.onLaunch
中请求主数据,那么每个页面的请求逻辑都需要在放在App
的生命周期中,这样不仅造成了逻辑的耦合,也将一定程度影响初始数据initData
的发送,继而拖慢渲染层的初次渲染。 - 如果在
Page.onLoad
中请求主数据,那么必须要等到渲染层完成firstRender
之后才能请求主数据,时机比较晚。
onInit 简介
小程序提供一种页面级别的生命周期Page.onInit
。
该生命周期的执行时机介于App.onLaunch
和Page.onLoad
之间。具体的执行时机可参考下图,小程序是在setInitData
之后立即执行Page.onInit()
。
如果把主数据请求从 Page.onLoad
转移到 Page.onInit
中,将极大提升小程序的页面加载性能。
开发者可以在onInit
中向服务器请求数据,并执行setData
。图中展示了setData
的两种时机,同时应注意:
- 如果开发者
setData
发出的时机早于渲染线程的 FCP ,那么在onLoad
中将能获取到本次setData
的视图信息。 - 如果
setData
晚于 FCP ,那么onLoad
中将获取不到本次setData
的视图信息。
由于
App.onShow
是依赖客户端通知逻辑线程的,所以Page.onInit
不一定会在首次App.onShow
之后执行。
可确定的前后顺序是:App.onLaunch
->Page.onInit
->Component.created
->Component.attached
->Page.onLoad
如何使用 onInit
尽量将页面的业务数据请求放在Page.onInit
中。
- JS
function getData(param) {
return new Promise((resolve, reject) => {
swan.request({
url: 'xxx',
success: res => resolve(res)
});
});
}
Page(
// 使用一个标记位,确保只请求一次主数据
hasRequest: false
data: {
value: ''
},
onInit(param) {
if (!this.hasRequest) {
this.hasRequest = true;
getData(param).then(res => {
this.setData({
value: res.data
});
})
}
},
onLoad(param) {
if (!this.hasRequest) {
this.hasRequest = true;
getData(param).then(res => {
this.setData({
value: res.data
});
})
}
}
);
使用建议
- 不能进行任何依赖视图层的操作,包括且不限于:
selectComponent
、selectAllComponents
、swan.createSelectorQuery
、swan.createMapContext
、swan.createCameraContext
、swan.createCanvasContext
等; - 由于并非所有版本的基础库都支持此生命周期,开发者可以参考上述代码片段,增加兼容逻辑;
- 如果您的小程序在逻辑线程初始化阶段存在较大瓶颈,那么使用
Page.onInit
可能不会有明显效果。建议从减少动态库和插件的使用、减少App.onLaunch
耗时等角度进行优化。
onInit 的收益
此处以百度知道、百度百科和宝宝知道小程序为例使用Page.onInit
进行了优化,上屏时长均有明显提升。
以下是三个小程序把主数据请求从Page.onLoad
迁移到Page.onInit
后,获取到的收益:
小程序 | 收益(单位:ms) |
---|---|
百度知道 | 210 |
百度百科 | 100 |
宝宝知道 | 150 |