小编给大家分享一下小程序多图列表怎么实现性能优化,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
什么造成的性能问题
简单的来说: 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"
>
</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>
以上是“小程序多图列表怎么实现性能优化”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注天达云行业资讯频道!