本文首发于公众号「IT自学课堂」。
小程序在整个启动流程中,一般需要完成几项工作:
开发者可以在第2,3去优化小程序的启动性能。
小程序在首次打开时,会去下载并执行代码包,随着代码包大小的上升,耗时也会相应增加。开发者可以采取以下方案:
*提前首屏数据请求 *
大部分小程序在渲染首页时,需要依赖服务端的接口数据,小程序为开发者提供了提前发起数据请求的能力:
*缓存请求数据 *
小程序提供了wx.setStorageSync等异步读写本地缓存的能力,数据存储在本地,返回的会比网络请求快。如果开发者基于某些原因无法采用数据预拉取与周期性更新,我们推荐优先从缓存中获取数据来渲染视图,等待网络请求返回后进行更新。
*精简首屏数据 *
此外,我们推荐开发者延迟请求非关键渲染数据,缩短网络请求时延,与视图层渲染无关的数据尽量不要放在 data 中,加快首屏渲染完成时间。
避免阻塞渲染
在小程序启动流程中,会顺序执行app.onLaunch, app.onShow, page.onLoad, page.onShow, page.onReady,所以,尽量避免在这些生命周期中使用Sync结尾的同步API,如 wx.setStorageSync,wx.getSystemInfoSync 等。
渲染时间指的是首次渲染或因数据变化带来的页面结构变化的渲染花费的时间。
渲染界面的耗时过长会让用户觉得卡顿,体验较差,出现这一情况时,需要校验下是否同时渲染的区域太大(例如列表过长),或渲染依赖的计算是否过于复杂。
自定义组件的更新只在组件内部进行,不受页面其他不能分内容的影响;比如一些运营活动的定时模块可以单独抽出来,做成一个定时组件,定时组件的更新并不会影响页面上其他元素的更新;各个组件也将具有各自独立的逻辑空间。每个组件都分别拥有自己的独立的数据、setData调用。
脚本执行时间是指JS脚本在一次同步执行中消耗的时间,比如生命周期回调、事件处理函数的同步执行时间。
执行脚本的耗时过长会让用户觉得卡顿,体验较差,出现这一情况时,需要确认并优化脚本的逻辑。
解决方案:一个执行周期内脚本运行时间不超过 1 秒。
小程序中可能有n个页面,所有的这些页面,虽然都拥有自己的webview(渲染层), 但是却共享同一个js运行环境。也就是说,当你跳到了另外一个页面(假设是B页面),本页面(假设是A页面)的定时器等js操作仍在进行,并且不会被销毁,并且会抢占B页面的资源。
解决方案:页面销毁时,先清除页面中的定时器;
视图层将事件反馈给逻辑层时,需要一个通信过程,通信的方向是从视图层到逻辑层。因为这个通信过程是异步的,会产生一定的延迟,延迟时间同样与传输的数据量正相关,数据量小于64KB时在30ms内。
解决方案:
scroll 事件,也是一次通讯,是webview层向js逻辑层的通讯。这次通讯也是开销较大,如果考虑到这个事件被频繁的调用,回调函数如果有复杂的setData的话,性能就会很差。
解决方案:
setData是小程序开发中使用最频繁的接口,也是最容易引发性能问题的接口。
setData接口的调用涉及逻辑层与渲染层间的线程通信,通信过于频繁可能导致处理队列阻塞,界面渲染不及时而导致卡顿,应避免无用的频繁调用。
解决方案:每秒调用setData的次数不超过 20 次。
由于小程序运行逻辑线程与渲染线程之上,setData的调用会把数据从逻辑层传到渲染层,数据太大会增加通信时间。
解决方案:
setData操作会引起框架处理一些渲染界面相关的工作,一个未绑定的变量意味着与界面渲染无关,传入setData会造成不必要的性能消耗。
解决方案:setData传入的所有数据都与页面渲染有关,对于与页面渲染无关的变量,可以挂在页面或者组件的this对象上。
建议一个页面使用少于 1000 个 WXML 节点,节点树深度少于 30 层,子节点数不大于 60 个。一个太大的 WXML 节点树会增加内存的使用,样式重排时间也会更长,影响体验。
解决方案:页面WXML节点少于 1000 个,节点树深度少于 30 层,子节点数不大于 60 个。
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。
当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
解决方案:
开启 HTTP 缓存控制后,下一次加载同样的图片,会直接从缓存读取,大大提升加载速度。
解决方案:所有图片均开启 HTTP 缓存
图片太大会增加下载时间和内存的消耗,应根据显示区域大小合理控制图片大小。
解决方案:如果储存图片的cdn支持,可以对图片进行剪裁,使请求的图片宽高都不超过实际显示宽高的3倍。
使用 HTTPS,可以让你的小程序更加安全,而 HTTP 是明文传输的,存在可能被篡改内容的风险;
解决方案:使用 getHttpsImgUrl 函数,获取图片安全域名。
function getHttpsImgUrl(url = '') {
if(!url) return url;
var httpsUrl = '';
var domain = url.split('://')[1];
httpsUrl = 'https://' + domain;
return httpsUrl;
}
module.exports = {
getHttpsImgUrl: getHttpsImgUrl
};
短时间内发起太多图片请求会触发浏览器并行加载的限制,可能导致图片加载慢,用户一直处理等待。
解决方案:应该合理控制数量,可考虑使用雪碧图技术或在屏幕外的图片使用懒加载,保证每秒发起的图片请求数不超过 20 个。
图片若没有按原图宽高比例显示,可能导致图片歪曲,不美观,甚至导致用户识别困难。
解决方案:可根据情况设置 image 组件的 mode 属性,以保持原图宽高比。
短时间内发起太多请求会触发小程序并行请求数量的限制,同时太多请求也可能导致加载慢等问题
解决方案:合理控制请求数量,甚至做请求的合并等,保证每秒通过wx.request发起的请求数不超过 10 个。
请求的耗时太长会让用户一直等待甚至离开。
解决方案:应当优化好服务器处理时间、减小回包大小,让请求快速响应,保证网络正常的情况下,所有网络请求都在 1 秒内返回结果。
发起网络请求总会让用户等待,可能造成不好的体验,应尽量避免多余的请求。
解决方案:对同样的请求进行缓存,3 分钟以内同一个url请求不出现两次回包大于 128KB 且一模一样的内容。
大量未使用的样式,会增加小程序包体积大小,从而在一定程度上影响加载速度。
解决方案:
惯性滚动会使滚动比较顺畅,在安卓下默认有惯性滚动,而在 iOS 下需要额外设置 -webkit-overflow-scrolling: touch
的样式。
我们应该合理地设置好可点击元素的响应区域大小,如果过小会导致用户很难点中,体验很差。
底部的可交互组件如果渲染在iPhone X的安全区域外,容易误触Home Indicator 。
关注我,一周3篇干货文章与你分享。