小程序多图列表实现性能优化的方法步骤


Posted in Javascript onMay 28, 2019

写这篇文章的缘由: 最近在公司的小程序项目中遇到了页面图片元素过多导致的性能问题. 从小程序提供的性能检测面板分析, 确定是图片元素占用了过多内存导致.

因为本人之前主要是做桌面端应用开发和原生app开发, 没有太顾及过移动端图片的内存占用问题. 这次既然遇到了, 也就趁这个机会学习一下其优化的技巧.

什么造成的性能问题

简单的来说: DOM节点过多 && 图片节点过多

DOM节点过多会造成更多的内存占用. 按照目前的微信小程序限制, 内存占用500M以上会出现卡顿, 甚至闪退. 如果列表中节点没有图片标签, 内存占用现象还不会太明显, 只是DOM节点过多会造成页面渲染耗时增加. 但当列表节点中含有图片时, 内存占用会迅速攀升.

如何解决这两点呢?

对于上面两点, 我们分别有对应的优化思路

1. DOM节点过多.

对于无限加载的页面, 列表中每一个元素都有大量的子节点. 当列表数目增加时, 页面的总节点数会暴增. 以小红书的小程序为例:

小程序多图列表实现性能优化的方法步骤

上图中可以看到, 该页面为很多的卡片组成的列表页面. 假设一个卡片的DOM子节点数为 30, 那么当列表元素加载到1000时, 页面会有 30 * 1000 = 30,000 个DOM节点. 小程序显然是吃不消的 (注: 微信小程序推荐总节点数不超过: 1000)

那我们该如何处理来减少节点数呢?

思路很简单: 我们可以只对用户当前屏幕和上下两屏进行真实内容的加载, 对于其他用户暂时不可见的地方, 用空白的节点进行占位. 这样处理后, 实际有内容的卡片数目不超过5个, 页面的总节点数为: 5 * 30 + 995 = 1145. 相对于之前的节点数有了巨大的改进.

让我们来看看代码的实现

写代码前, 让我们整理一下需要的数据结构.

首先这是一个列表页面, 我们需要一个 List来保存页面显示的数据: showCards. showCards 中只会保存5条真实数据, 其余数据展示以空对象填充.

我们还需要一个保存所有真实数据的List, 这样当用户滑动页面时, 我们才能实时获取需要显示的卡片真实数据: totalCards

Page({
 showCards: [],
 totalCards: []
})

接下来我们来写页面布局部分:

<view wx:for="{{showCards}}"
    wx:key="{{index}}">

  <self-define-component data-card-data="{{item}}">
  </self-define-component>
  
</view>

简单的代码框架就是这样 (这里省略了很多不影响理解思路的代码细节)

我们先实现没有优化DOM节点的代码逻辑. 在页面滑动到最底部时, 向showCards push进新的卡片, 并通过 setData 更新页面. 这样就实现了一个简单的下拉无限加载的列表页面.

async onReachBottom() {
  const newCards = await fetchNewCards();
 this.data.showCards.push(newCards);
 this.setData({
  showCards: this.data.showCards
 })
},

接下来我们实现优化DOM节点的代码逻辑. 我们会再用户滑动页面(onScroll事件) 时, 对当前页面每个card 的位置进行判断, 如果该 card在用户可见范围内的上下两屏内, 则展示真实数据, 否则将其替换为宽高与原卡片一致的空白占位节点.

在 Page 的 onPageScroll 回调中, 我们进行回收函数的调用 (注意这里回调时要进行节流处理, 否则频繁调用会导致页面闪动) . 让我们看看这个回收页面节点函数的主要逻辑:

回调中, 我们首先通过小程序提供的获取页面元素位置的api: createSelectorQuery().boundingClientRect 来拿到每个卡片的位置信息.

接下来, 我们通过位置信息, 判断是否展示card的真实数据. 对于不展示真实数据的card, 我们需要保存其高度信息, 以便在渲染页面时使用高度信息填充页面. 同时我们给空card一个 type 属性, 方便我们在 wxml中渲染时判断卡片类型.

async onScrollCallback() {
 try {
  const rectList = await this.calcCardsHeight();
  this.recycleCard(rectList);
 } catch (e) {
  console.error(e);
 }
}
 calcFeedHeight() {
  return new Promise((resolve, reject) => {
   this.createSelectorQuery()
    .selectAll(`.card`)
    .boundingClientRect(rectList => {
     resolve(rectList);
    })
    .exec()
  })
 },

 recycleCard(rectList) {
  const newShowCards = [];
  for (let i = 0; i < this.data.showCards.length; i++) {
   const rect = rectList[i];
   if (rect && Math.abs(rectList[i].top - 0) > pageHeight * 2) {
    newShowCards.push({
     type: 'empty-card',
     height: rectList[i].bottom - rectList[i].top
    });
   } else {
     const feed = totalCards[i];
    newShowCards.push(feed);
   }
  }
  this.setData({
   showCards: newShowCards
  });
 }

接下来, 我们要对wxml布局文件进行相应的修改:

<view wx:for="{{showCards}}"
    wx:key="{{index}}">

  <view wx:if="{{item.type === 'empty-card'}}"
     class="card empty-card"
     style="height: {{item.height}}px">
  </view>

  <self-define-component wx:if="{{item.type !== 'empty-card'}}"
        data-card-data="{{item}}"
        class="card read-card">
  </self-define-component>
  
 </view>

这样, 我们就解决了 DOM节点数目过多的问题. 并且最大限度的不影响用户的体验. (虽然用户快速上下滑动时还是会看到一些空白, 但大多数情况用户不会非常快速的上下滑, 而是阅读内容并慢速滑动)

2. 图片节点过多

通过上面一步的优化, 我们其实已经大幅减少了页面加载的图片数目. 但是有些情况, 我们的列表中的每一个卡片并不是只有一张图, 有时我们的图片组件是一个 swiper. 我们假设每个swiper平均展示10张图片, 那么我们展示5张card的话,<Image/> 节点就有 50 个. 对于一些低端的安卓机, 这样的开销依然会造成卡顿.

那有什么好的优化方案呢? 前面一个问题, 我们的优化思路是在用户看不见的地方, 将节点简化展示.

同样的, 对于swiper控件, 用户能看到的也就是当前图片 以及 滑动可见的左右两张图片. 其余位置的图片是可以简化展示的. 从下图可以看到, 其实需要立即加载的图片只有三张. (红色的框代表的是swiper组件的可视区域)

小程序多图列表实现性能优化的方法步骤

我们使用一个变量记录当前swiper控件展示图片的坐标: curIndex, 然后我们改造一下 wxml布局文件. 代码逻辑很简单, 就是通过判断当前Image 节点的index和swiper展示节点的 index之间距离, 大于2就不显示.

经过这样的处理后, 我们的每个swiper组件, 最多只会有三个占用实际内存的 <Image/> 节点.

<swiper-item wx:for="{{images}}"
          wx:key="{{index}}">

    <view >
     <image class="img"
         mode="widthFix"
         src="{{index - curIndex < 2 && index - curIndex > -2 ? item.url : ''}}">
     </image>
    </view>
   </swiper-item>

最后

以上就是我在这次性能优化中用到的一些小技巧, 希望能为你带来一些帮助 :)

如果你对我的文章感兴趣, 这里有我的一些 数据可视化, D3.js 方面的文章, 欢迎 fork && star:

https://github.com/ssthouse/ssthouse-blog

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS查看对象功能代码
Apr 25 Javascript
js 操作select相关方法函数
Dec 06 Javascript
js中 关于undefined和null的区别介绍
Apr 16 Javascript
javascripit实现密码强度检测代码分享
Dec 12 Javascript
jquery中animate的stop()方法作用实例分析
Jan 30 Javascript
Jquery中Event对象属性小结
Feb 27 Javascript
Vue数据驱动模拟实现1
Jan 11 Javascript
基于JavaScript实现拖动滑块效果
Feb 16 Javascript
jQuery插件HighCharts实现2D柱状图、折线图的组合多轴图效果示例【附demo源码下载】
Mar 09 Javascript
JavaScript累加、迭代、穷举、递归等常用算法实例小结
May 08 Javascript
Vue.js中Line第三方登录api的实现代码
Jun 29 Javascript
vue cli4.0项目引入typescript的方法
Jul 17 Javascript
实例详解带参数的 npm script
May 28 #Javascript
jquery实现Ajax请求的几种常见方式总结
May 28 #jQuery
Vue2.x通用条件搜索组件的封装及应用详解
May 28 #Javascript
jquery操作select常见方法大全【7种情况】
May 28 #jQuery
vue实现条件叠加搜索的解决方法
May 28 #Javascript
webpack4 从零学习常用配置(小结)
May 28 #Javascript
详解ES6 export default 和 import语句中的解构赋值
May 28 #Javascript
You might like
基于php常用函数总结(数组,字符串,时间,文件操作)
2013/06/27 PHP
thinkphp的CURD和查询方式介绍
2013/12/19 PHP
PHP实现图片裁剪、添加水印效果代码
2014/10/01 PHP
PHP实现的下载远程文件类定义与用法示例
2017/07/05 PHP
PHP PDOStatement::fetchObject讲解
2019/02/01 PHP
通过JS判断联网类型和连接状态的实现代码
2015/04/01 Javascript
JS处理json日期格式化问题
2015/10/01 Javascript
谈谈我对JavaScript DOM事件的理解
2015/12/18 Javascript
基于Nodejs利用socket.io实现多人聊天室
2017/02/22 NodeJs
jQuery插件zTree实现删除树子节点的方法示例
2017/03/08 Javascript
vue实现百度搜索功能
2020/12/28 Javascript
JS Thunk 函数的含义和用法实例总结
2020/04/08 Javascript
[01:46]2020完美世界全国高校联赛秋季赛报名开启
2020/10/15 DOTA
Python中bisect的用法
2014/09/23 Python
Python中的字符串替换操作示例
2016/06/27 Python
python3+PyQt5泛型委托详解
2018/04/24 Python
Python数据分析:手把手教你用Pandas生成可视化图表的教程
2018/12/15 Python
Python数据结构与算法(几种排序)小结
2019/06/22 Python
python多线程http压力测试脚本
2019/06/25 Python
python实现发送form-data数据的方法详解
2019/09/27 Python
python实现超市商品销售管理系统
2019/11/22 Python
Python select及selectors模块概念用法详解
2020/06/22 Python
Python自动发送和收取邮件的方法
2020/08/12 Python
Python爬虫自动化获取华图和粉笔网站的错题(推荐)
2021/01/08 Python
python drf各类组件的用法和作用
2021/01/12 Python
意大利奢侈品网站:Italist
2016/08/23 全球购物
怎样在 Applet 中建立自己的菜单(MenuBar/Menu)?
2012/06/20 面试题
个人党性剖析材料
2014/02/03 职场文书
党员干部反四风民主生活会对照检查材料思想汇报
2014/10/12 职场文书
2014年文明创建工作总结
2014/11/25 职场文书
中考学习决心书
2015/02/04 职场文书
上市公司财务总监岗位职责
2015/04/03 职场文书
新学期开学标语2015
2015/07/16 职场文书
公司老总年会致辞
2015/07/30 职场文书
学习党史心得体会2016
2016/01/23 职场文书
SpringBoot 拦截器妙用你真的了解吗
2021/07/01 Java/Android