首屏渲染优化
小程序初次打开时的白屏阶段,是指小程序代码包下载完(也就是启动界面结束)之后,页面完成首屏渲染的这一阶段,也就是 FMP (首次有效绘制)。
FMP 没法用标准化的指标定义,但对于大部分小程序来说,页面首屏展示的内容都需要依赖服务端的接口数据,那么影响白屏加载时间的主要由这两个元素构成:
- 网络资源加载时间
- 渲染时间
启用本地缓存
小程序提供了读写本地缓存的接口 https://developer.tuya.com/cn/miniapp/a ... et-storage,数据存储在设备硬盘上,除此之外,缓存数据还可以作为兜底数据,避免出现接口请求失败时页面空窗。但并非所有场景都适合缓存策略,譬如对数据即时性要求非常高的场景(如抢购入口)来说,展示老数据可能会引发一些问题。
数据隔离:智能小程序目前会默认按照 uid和appId两个维度 对缓存空间进行隔离,业务可自行根据业务需求根据countryCode、家庭ID等维度进行再隔离,避免数据误展示。
跳转时预拉取
为了尽快获取到服务端数据,比较常见的做法是在页面 onLoad 钩子被触发时发起网络请求,但其实这并不是最快的方式。从发起页面跳转,到下一个页面 onLoad 的过程中,小程序需要完成一些页面实例化的工作,耗时大概为 300ms左右。
实际上,我们可以在发起跳转前(如 navigateTo 调用前),提前请求下一个页面的主接口并存储在 globalData中,待下个页面加载完成后从 globalData 对象中读取数据即可。
这也是双线程模型所带来的优势之一,不同于多页面 web 应用在页面跳转/刷新时就销毁掉 window 对象。
分步渲染
保证重要信息优先展示
保证次要信息占位图
图片懒加载
小程序image在组件级别提供了图片lazy-load的能力。
骨架屏
业务数据请求回包之前使用骨架屏占位,提升体验。
运行时的优化
从上述表格中可以看出,在小程序的双线程通信模式,数据量在一定程度上,会指数级上升。
所以运行时的优化最主要的原则就是减少通信频率,降低通信数据量。
setData
业务开发阶段,开发者可以控制 setData 的频率,尽可能合并数据,减少调用次数。
开发者应该尽量减小调用 setData 的数据量,来提升通信效率。
只把渲染相关的数据放在data中。
去掉不必要的事件绑定
当用户事件(如 Click、Touch 事件等)被触发时,视图层会把事件信息反馈给逻辑层,这也是一个线程间通信的过程。但,如果没有在逻辑层中绑定事件的回调函数,通信将不会被触发。
所以,尽量减少不必要的事件绑定,尤其是像 onPageScroll 这种会被频繁触发的用户事件,会使通信过程频繁发生。
注意:Touch事件,使用前要思考是否必须要绑定该事件到逻辑层触发,是否可以在SJS中处理该事件。
去掉不必要的节点属性
组件节点支持附加 自定义数据 dataset,当用户事件被触发时,视图层会把事件 target 和 dataset 数据传输给逻辑层。那么,当自定义数据量越大,事件通信的耗时就会越长,所以应该避免在自定义数据中设置太多数据。
Code: Select all
<view class="title">弹出位置</view>
<view class="box">
<button class="btn" bindtap="popup" data-position="right">右侧弹出</button>
<button class="btn" bindtap="popup" data-position="top">顶部弹出</button>
<button class="btn" bindtap="popup" data-position="bottom">底部弹出</button>
<button class="btn" bindtap="popup" data-position="center">中央弹出</button>
</view>
Code: Select all
changeOverlayStyle(e) {
let overlayStyle = ''
const type = e.currentTarget.dataset.type
switch (type) {
case 'black':
overlayStyle = 'background-color: rgba(0, 0, 0, 0.7)'
break
case 'white':
overlayStyle = 'background-color: rgba(255, 255, 255, 0.7)'
break
case 'blur':
overlayStyle = 'background-color: rgba(0, 0, 0, 0.7); filter: blur(4px);'
}
this.setData({ overlayStyle, show: true })
},
SJS使用
SJS 将一些视图层需要计算的能力放在SJS中操作。
SJS运行在视图层
SJS可以处理视图层绑定的事件且可以获取当前所在实例的部分能力。https://developer.tuya.com/cn/miniapp/f ... /event/sjs
SJS并不是完全的JavaScript,仅具有部分Safe的能力。
处理事件、工具函数,无需通信。
Render Script
渲染脚本,可用于处理高频的绘图需求,可以提高视图的动画渲染性能,主要应用场景 canvas 图表渲染,webGL 图形渲染等。
关于RJS的问题可以查看这里哦: https://tuyaos.com/viewtopic.php?p=884&hilit=RJS#p884
销毁持久化内存
根据双线程模型,小程序每一个页面都会独立一个 webview 线程,但逻辑层是单线程的,也就是所有的 webview 线程共享一个 JS 线程。以至于当页面切换到后台态时,仍然有可能抢占到逻辑层的资源,譬如没有销毁的 setInterval、setTimeout 定时器:
即使如小程序的 <swiper> 组件,在页面进入后台态时依然是会持续轮播的。
正确的做法是,在页面 onHide 的时候手动把定时器清理掉,有必要时再在 onShow 阶段恢复定时器。
坦白讲,区区一个定时器回调函数的执行,对于系统的影响应该是微不足道的,但不容忽视的是回调函数里的代码逻辑,譬如在定时器回调里持续 setData 大量数据,这就非常难受了。
去掉调试工具VConsole
VConsole 是挂载到视图层的调试工具,逻辑层的日志会通过通道发到视图层,日志打印频繁可能会阻塞通道,遇到性能要求较高的页面调试,去掉vconsole调试工具,减少通道占用。
性能指标
首屏时间不超过 5 秒;
渲染时间不超过 500ms;
每秒调用 setData 的次数不超过 20 次;
setData 的数据在 JSON.stringify 后不超过 256kb;
页面 TYML 节点少于 1000 个,节点树深度少于 30 层,子节点数不大于 60 个;
所有网络请求都在 1 秒内返回结果;