# 实战篇 7:对小程序进行优化
# 页面流程优化
合理的页面流程可以加快页面打开速度,下面介绍几种常见的方式。
# 减少白屏时间
当进入的页面还在进行数据加载的时候,如果加载时间过长,用户看不到页面实际内容,看到的只是一个白屏界面。可以通过增加骨架屏(Skeleton Screen)或者默认数据来减少白屏时间。在新鲜天气的天气预报页面,获取数据的流程较长,笔者通过在首屏提前渲染默认数据来减少白屏时间,效果如下图所示。

当然这个骨架屏和默认数据做得还不够细致,有兴趣的读者可以通过 GitHub 上的源码继续优化。
# 利用逻辑层空闲时间预加载数据
小程序是由逻辑层和视图层共同作用的,逻辑层代码(App Service)在小程序执行的生命周期内会常驻内存,并不会因为切换页面而释放资源,利用这个特点,可以对页面流程进行一些优化。
新鲜天气是由天气预报和心情签到两个页面组成的,在天气预报页面数据获取结束之后、页面空闲之时,如果用户已经授权登录信息,那么可以提前获取心情签到页面的数据,将数据存入 app.js 的 globalData 中,当用户进入心情签到页时,就可以减少等待时间,很快看到页面内容了。
天气预报页面对心情签到页面数据预取逻辑如下:
// weather/index.js
let prefetchTimer
const app = getApp()
Page({
onHide(){
// 切走,则清理计时器
clearTimeout(prefetchTimer)
},
onShow(){
// 显示则添加计时器
this._setPrefetchTimer()
},
_setPrefetchTimer(){
// 10s 预取
if(!app.globalData.currentMonthData.length && isUpdate){
prefetchTimer = setTimeout(() => {
this.prefetch()
}, 10e3)
}
},
prefetch(){
let openid = wx.getStorageSync('openid')
if(openid){
// 存在则预取当前时间的心情
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
getEmotionByOpenidAndDate(openid, year, month)
.then((r) => {
const data = r.data || []
// console.log(data)
app.globalData.currentMonthData = data
}).catch(e=>{})
}
}
})
在上面的预取代码中有个小技巧,就是增加了定时器清理功能,当页面切走(onHide)跳转到心情签到页面时,在后台预取数据已经变得没有意义了,所以及时地清理了定时器;而当页面再切回(onShow)的时候又重新启动了定时器,当然定时器的启动是以 globalData 没有数据,并且天气页面已经完成渲染为前提的。在心情签到页面中,获取的心情数据要存入 globalData,这样数据就打通了。
除了预取下一页的数据,如果整个项目中有较多的静态外链资源需要加载,也可以在首页空闲的时候进行预取。
# 默认数据缓存
对于用户第一次进入小程序的数据可以使用默认数据来构建骨架屏,空闲时预取下一页数据;而用户再次进入小程序,有了上一次的数据了,就可以使用之前的数据优先展示,等数据更新后重新渲染页面即可。心情签到数据也是这样,对于一个用户不能更改之前的签到数据,可以将这些数据存入小程序的本地缓存,减少 SQL 请求。要记录上次的数据,可以使用小程序的数据缓存 API。
// weather/index.js
// render 之后缓存数据
dataCache() {
const {current, backgroundColor, backgroundImage, today, tomorrow, address, tips, hourlyData} = this.data
wx.setStorage({
key: 'defaultData',
data: {
current,
backgroundColor,
backgroundImage,
today,
tomorrow,
address,
tips,
hourlyData
}
})
},
// onLoad 内部获取数据之前调用
setDataFromCache() {
wx.getStorage({
key: 'defaultData',
success: ({data}) => {
if (data && !isUpdate) {
// 存在并且没有获取数据成功,那么可以给首屏赋值上次数据
const {current, backgroundColor, backgroundImage, today, tomorrow, address, tips, hourlyData} = data
this.setData({
current,
bac