评判小程序页面性能
由于小程序开发环境的特殊性, 我们不能像普通网页那样通过 Chrome 开发工具或者一些成熟的性能测试工具 (例如 Lighthouse) 来了解一个页面的性能, 但微信官方提供了一个性能评分的工具, 点击这里可以查看工具详情.
体验评分是一项给小程序的体验好坏打分的功能, 它会在小程序运行过程中实时检查, 分析出一些可能导致体验不好的地方, 并且定位出哪里有问题, 以及给出一些优化建议.
后面我会以一个实际的例子来展示如何通过该工具来优化页面性能, 我们先看下我们页面优化前的一个评分情况.
存在 setData 的数据过大
<figcaption></figcaption>
我们的功能里面有个滚动到底部加载的功能, 优化前我们的做法是这样的
- <!-- 只阐述逻辑, 非真实代码 -->
- // 1: 初始一个 list, 存储列表数据
- data = startList
- // 2: 监听滚动事件, 滚动到底部获取新数据, 并追加到 list 尾部, 最后重新 setData
- onReachBottom:()=>{
- const {list} = this.data
- fetchNewData().then((res)=>{
- list.push(res.list);
- this.setData({list})
- }
}/ 在此我向大家推荐一个前端全栈开发交流圈: 582735936 突破技术瓶颈, 提升思维能力
我估计大部分人面对长列表滚动的时候, 一开始的处理方式都是这样的, 如果数据不多, 只有几页可能不会太暴露问题, 如果页数过多, 几十页甚至上百页的情况, list 的数据会越来越大, 每次 setData 的数据就会越来越多, 因而每次页面重新渲染的节点就会越来越多, 从而导致滚动到后面, 加载越来越慢. 另外, 由于小程序的视图渲染层和数据逻辑处理层是分开的, 不是在同一个线程上面的, 从用户触发页面交互, 到处理数据逻辑, 最后层现页面, 数据到视图是需要传输的, 因而小程序本身对数据大小也有限制, 不能超过 1M.
setData 数据路径
怎么解决呢? 小程序 setData 里面的 key 支持数据路径的写法, 比如
- let o = obj;
- this.setData({
- 'o. 属性':value
- })
或者
- let a = array;
- this.setData({
- 'array[0].text':value
})/ 在此我向大家推荐一个前端全栈开发交流圈: 582735936 突破技术瓶颈, 提升思维能力
所以我们可以通过数据路径的写法, 来将数据分批的传输到视图层中, 减少一次性 setData 的数据大小. 具体写法如下
- // 1. 通过一个二维数组来存储数据
- let feedList = [[array]];
- // 2. 维护一个页面变量值, 加载完一次数据 page++
- let page = 1
- // 3. 页面每次滚动到底部, 通过数据路径更新数据
- onReachBottom:()=>{
- fetchNewData().then((newVal)=>{
- this.setData({
- ['feedList[' + (page - 1) + ']']: newVal,
- })
- }
- }
- // 4. 最终我们的数据是 [[array1],[array2]] 这样的格式, 然后通过 wx:for 遍历渲染数据
存在短时间内发起太多图片请求(图片懒加载)
这个应该好理解, 就是渲染页面时, 一次性发送了过多的图片请求, 导致了同一时间发起了过多的 http 请求, http 连接是非常耗时的, 尤其是一次性发起这么多, 并且一次性发起的 http 链接也是有限制的, 比如 Chrome 浏览器就限制一次性最多 6 个.
所以在渲染页面时, 不在视图范围内的图片我们不加载, 只有元素出现在视图范围内了, 再渲染.
常规的做法是, 通过 getBoundingClientRect()获取元素的位置, 然后与页面滚动位置比较, 如果出现在视图内, 就将 img 显示. 这种方式有 2 个问题
getBoundingClientRect()方法调用本身容易引起页面重排
监听滚动事件本身就频繁触发, 虽然可以通过节流的方式来减少, 但还是容易增加无谓代码处理
IntersectionObserver
其实, 微信提供了 IntersectionObserver 对象.
IntersectionObserver 对象, 用于推断某些节点是否可以被用户看见, 有多大比例可以被用户看见
通过这个 API 我们不用再主动去监听元素位置了, 在页面渲染一开始, 通过这个 API 指明需要监听的元素, 系统会自动去监听了元素位置.
- let data = list;
- <img class="img-{{index}}" wx:for="{{data}}"></img>
- data.forEach((item,index)=>{
- this.createIntersectionObserver().relativeToViewport.observe(`.img-${index}`,res=>{
- if (res.intersectionRatio> 0){
- this.setData({
- item.imgShow:true
- })
- }
- })
- })
intersectionRatio 值大于 0, 说明元素出现在视图中了, 重新 setData 数据, 显示图片组件.
存在图片太大而显示区域过小
这个问题就是指图片尺寸太大了, 而页面上我们显示的尺寸又太小了, 图片尺寸大, 请求图片就越慢, 导致页面渲染速度下降.
CDN 图片处理
对于页面里面的图片, 最好都把图片存储在 cdn 服务器上, 一个是能充分利用 cdn 缓存来加快请求速度, 另外一个就是 cdn 上能够将图片进行一定的处理, 比如裁剪. 我司就是通过 cdn 来响应图片处理, 然后请求图片时告诉 cdn 服务器需要什么要的尺寸图片, 由 cdn 服务器响应对应尺寸图片.
key 值在列表渲染中的作用
key 值在列表渲染的时候, 能够提升列表渲染性能, 为什么呢? 首先得想想小程序的页面是如何渲染的, 主要分为以下几步:
将 wxml 结构的文档构建成一个 vdom 虚拟数
页面有新的交互, 产生新的 vdom 数, 然后与旧数进行比较, 看哪里有变化了, 做对应的修改 (删除, 移动, 更新值) 等操作
最后再将 vdom 渲染成真实的页面结构
key 值的作用就在第二步, 当数据改变触发渲染层重新渲染的时候, 会校正带有 key 的组件, 框架会确保他们被重新排序, 而不是重新创建, 以确保使组件保持自身的状态, 并且提高列表渲染时的效率.
key 值如果不指明, 默认会按数组的索引来处理, 因而会导致一些类似 input 等输入框组件的值出现混乱的问题.
相关测试代码可以查看: wxkey https://developers.weixin.qq.com/s/ZqKQ6hmM727z
可以看到
不加 key, 在数组末尾追加元素, 之前已渲染的元素不会重新渲染. 但如果是在头部或者中间插入元素, 整个 list 被删除重新渲染, 且 input 组件的值还出现了混乱, 值没有正常被更新
添加 key, 在数组末尾, 中间, 或者头部插入元素, 其它已存在的元素都不会被重新渲染, 值也能正常被更新
因而, 在做 list 渲染时, 如果 list 的顺序发生变化时, 最好增加 key, 且不要简单的使用数组索引当做 key.
最后看看我们的成果:
体验码:
希望今天我的分享能对您优化小程序页面有一定的启示, 创造出性能更好更流畅的页面.
结语
感谢您的观看, 如有不足之处, 欢迎批评指正.
获取资料
来源: http://www.qdfuns.com/article/51786/a7fed1387c6af8b9eab12dcf825f0d5c.html