微信小程序Skyline模式本身虚拟列表文档:
Skyline / list-builder (qq.com) 普通列表模式
Skyline / grid-builder (qq.com) 瀑布流列表模式
微信小程序Skyline本身是有更有效的解决方式,以下方式仅供学习。
微信小程序长列表,渲染的越多就会导致内存吃的越多。特别是长列表的图片组件和广告组件。
为了解决内存问题,所以看了很多人的资料,都不太符合通用的解决方式,很多需要固定子组件高度,但是瀑布流是无法固定的,所以需要找更好的方式。好在有一篇可以借鉴的文章在其基础上做了修改,解决了内存问题!
借鉴了以下文章的解决方式,由于借鉴章依旧存在内存泄漏问题,所以本文章改进后不再内存泄漏
借鉴文链接:解决小程序渲染复杂长列表,内存不足问题 - 掘金 (juejin.cn)
进入下面小程序可以体验效果:
代码效果图
老规矩,直接上代码,各位直接引用!
一、定义骨架组件
WXML:
<view class="list-item" id="list-item-{{skeletonId}}" style="min-height: {{height}}px;">
<block wx:if="{{showSlot}}">
<view style="height: 1px; z-index: 1;">{{parentIndex}},{{index}}</view>
<image style="width: 100%;height: 100%;" mode="aspectFill" src="https://i-blog.csdnimg.cn/blog_migrate/d28685ab59df83238b7e06d2e239e3ed.jpeg"></image>
</block>
</view>
<!-- 广告,不用可以直接去掉 -->
<block wx:if="{{(index+1)%12==0}}">
<view class="adbk" style="min-height: {{(shkey==='list'||shkey==='tx')?'330px':'315px'}};{{showSlot?'background:#ffff':''}}">
<block wx:if="{{showSlot}}">
<ad-custom
class="girdAd"
unit-id="adunit-xxxxx"></ad-custom>
</block>
</view>
</block>
JS:
// components/skeleton.js
let app = getApp()
Component({
lifetimes:{
created(){
//设置一个走setData的数据池
this.extData = {
listItemContainer: null,
}
},
attached(){
},
detached() {
try {
this.extData.listItemContainer.disconnect()
} catch (error) {
}
this.extData = null
},
ready() {
this.setData({
skeletonId: this.randomString(8), //设置唯一标识
color:this.randomColor()
})
wx.nextTick(() => {
// 修改了监听是否显示内容的方法,改为前后showNum屏高度渲染
// 监听进入屏幕的范围relativeToViewport({top: xxx, bottom: xxx})
let { windowHeight = 667 } = wx.getSystemInfoSync() //请自行优化这个取值
let showNum = 2 //超过屏幕的数量,目前这个设置是上下2屏
try {
this.extData.listItemContainer = this.createIntersectionObserver()
this.extData.listItemContainer.relativeToViewport({ top: showNum * windowHeight, bottom: showNum * windowHeight })
.observe(`#list-item-${this.data.skeletonId}`, (res) => {
let { intersectionRatio,isIntersecting } = res
if (intersectionRatio === 0&&!isIntersecting) {
// console.log('【卸载】', this.data.skeletonId, '超过预定范围,从页面卸载')
this.setData({
showSlot: false
})
} else {
// console.log('【进入】', this.data.skeletonId, '达到预定范围,渲染进页面')
this.setData({
showSlot: true,
height: res.boundingClientRect.height
})
}
})
} catch (error) {
console.log(error)
}
})
}
},
/**
* 组件的属性列表
*/
properties: {
parentIndex:{
type:Number,
value:0
},
index:{
type:Number,
value:0
}
},
/**
* 组件的初始数据
*/
data: {
height: 0, //卡片高度,用来做外部懒加载的占位
showSlot: true, //控制是否显示当前的slot内容
skeletonId: '',
color:'#7179b1',
colorList:[
'#7179b1',
'#d66f33',
'#33d665',
'#cc33d6',
'#7233d6',
'#338bd6',
'#b5d2ea',
'#6f0c0c',
'#d43f8b',
'#00ccec',
'#2e666f',
'#ffcd18'
]
},
/**
* 组件的方法列表
*/
methods: {
/**
* 生成随机的字符串
*/
randomString(len) {
len = len || 32;
var $chars = 'abcdefhijkmnprstwxyz2345678'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
var maxPos = $chars.length;
var pwd = '';
for (var i = 0; i < len; i++) {
pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
},
randomColor(){
let num = Math.ceil(Math.random()*10);
return this.data.colorList[num];
}
}
})
WXSS:
.girdAd{
border-radius: 10px;
z-index: 11;
}
.adbk{
background: #636363;
border: 1px solid #f3f3f3;
border-radius: 10px;
margin-top: 10px;
}
.adloading{
line-height: 300px;
color: rgb(255, 255, 255);
text-align: center;
position: absolute;
right: 23%;
}
二、业务代码使用骨架组件
业务代码中,就是数组的数据需要频繁的使用setData这个接口,所以需要避免频繁操作。
将list 数据改成二位数组。
json需要引入: "skeleton":"/components/skeleton/skeleton"
WXML:
<scroll-view
style="height: 100vh;"
type="custom"
scroll-y="{{true}}"
lower-threshold="{{100}}"
scroll-top="0"
scroll-with-animation="{{true}}"
bindscrolltolower="loadmore">
<grid-view type="masonry" cross-axis-count="{{2}}" cross-axis-gap="{{10}}" main-axis-gap="{{10}}" padding="{{[5,5,0,5]}}">
<block wx:for-item="parentItem" wx:for-index="parentIndex" wx:for="{{list}}" wx:key="{{parentIndex}}">
<!-- 这个view仅作为间隔区分展示用,并不是必须的 -->
<block wx:if="{{parentIndex!=0}}"
wx:for="{{parentItem}}"
wx:key="{{index}}" >
<!-- 使用 -->
<skeleton parentIndex="{{parentIndex}}" index="{{index}}"></skeleton>
</block>
</block>
</grid-view>
</scroll-view>
JS:
Component({
lifetimes:{
created(){
this.loadmore()
}
},
data: {
list: [[{}]]
},
methods: {
loadmore: function() {
//过长的list需要做二维数组,因为setData一次只能设置1024kb的数据量,如果过大的时候,就会报错
//二维数组每次只设置其中一维,所以没有这个问题
let nowList = `list[${this.data.list.length}]`
let demoList = this.getList(100)
this.setData({
[nowList]: demoList
})
},
/**
* 每次吸入num条数据
*/
getList(num) {
let list = []
for (let i = 0; i < num; i++) {
list.push({
height: this.getRadomHeight()
})
}
return list
},
/**
* 生成随机(100, 400)高度
*/
getRadomHeight() {
return parseInt(Math.random()*100 + 300)
}
},
})