微信小程序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://img-blog.****img.cn/direct/25e4d10c5187409d9190a2f47297503e.jpeg"></image>
    </block>
</view>
<!-- 广告,不用可以直接去掉 -->
<block wx:if="{{(index+1)%12==0}}">
    <view class="adbk" style="min-height: {{(shkey==='list'||shkey==='tx')?'330px':'315px'}};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 } = res
              if (intersectionRatio === 0) {
                // 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)
    }
  },
})

 

上一篇:基于离散差分法的复杂微分方程组求解matlab数值仿真


下一篇:Spring Boot核心注解大全:从入门到精通(三)