在 onInit 请求首屏主数据

基础库 3.160.12 及以上版本开始支持Page.onInit


大部分小程序,需要发起至少一次网络请求并调用 setData,才能完成整个页面的最终渲染。如果能优化该环节,页面的渲染时间将会大大缩短。

回顾一下我们在性能优化的原理和手段中所介绍的小程序启动流程,可以发现无论是把主数据请求放在 App.onLaunch 里还是 Page.onLoad 里,都会存在一些难以解决问题:

  1. 如果在 App.onLaunch 中请求主数据,那么每个页面的请求逻辑都需要在放在 App的生命周期中,这样不仅造成了逻辑的耦合,也将一定程度影响初始数据 initData 的发送,继而拖慢渲染层的初次渲染。
  2. 如果在 Page.onLoad 中请求主数据,那么必须要等到渲染层完成 firstRender 之后才能请求主数据,时机比较晚。

# onInit 简介

小程序提供一种页面级别的生命周期Page.onInit

该生命周期的执行时机介于 App.onLaunchPage.onLoad 之间。具体的执行时机可参考下图,小程序是在 setInitData 之后立即执行Page.onInit()

如果把主数据请求从 Page.onLoad 转移到 Page.onInit 中,将极大提升小程序的页面加载性能。

开发者可以在onInit中向服务器请求数据,并执行setData。图中展示了setData的两种时机,同时应注意:

  1. 如果开发者setData发出的时机早于渲染线程的 FCP,那么在onLoad中将能获取到本次setData的视图信息。
  2. 如果setData晚于 FCP,那么onLoad中将获取不到本次setData的视图信息。

由于App.onShow是依赖客户端通知逻辑线程的,所以Page.onInit 不一定会在首次 App.onShow 之后执行。
可确定的前后顺序是: App.onLaunch -> Page.onInit -> Component.created-> Component.attached -> Page.onLoad

# 如何使用 onInit

尽量将页面的业务数据请求放在Page.onInit中。

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
                });
            })
		}
	}
);

# 使用建议

  1. 不能进行任何依赖视图层的操作,包括且不限于:selectComponentselectAllComponentsswan.createSelectorQueryswan.createMapContextswan.createCameraContextswan.createCanvasContext等;
  2. 由于并非所有版本的基础库都支持此生命周期,开发者可以参考上述代码片段,增加兼容逻辑;
  3. 如果您的小程序在逻辑线程初始化阶段存在较大瓶颈,那么使用Page.onInit可能不会有明显效果。建议从减少动态库和插件的使用、减少App.onLaunch耗时等角度进行优化。

# onInit 的收益

此处以百度知道、百度百科和宝宝知道小程序为例使用 Page.onInit 进行了优化,上屏时长均有明显提升。

以下是三个小程序把主数据请求从Page.onLoad迁移到Page.onInit后,获取到的收益:

小程序 收益(单位 ms)
百度知道 210
百度百科 100
宝宝知道 150