likes
comments
collection
share

【微信小程序】setData数据量过大问题与分页刷新加载的实现

作者站长头像
站长
· 阅读数 13

我们在开发小程序时,会使用setData 函数将数据从逻辑层发送到视图层,同时改变对应的 this.data 的值(同步)。

setData介绍

Page.prototype.setData(Object data, Function callback)

Object 以 key: value 的形式表示,其中 key 可以以数据路径的形式给出,支持改变数组中的某一项或对象的某个属性,如 array[2].messagea.b.c.d,并且不需要在 this.data 中预先定义。

这个特殊用法下文有大用。

详细的setData API

setData的限制

  • 单次设置的数据不能超过1024kB,请尽量避免一次设置过多的数据
  • 每秒调用setData的次数不超过20次

setData接口的调用涉及逻辑层与渲染层间的线程通信,通信过于频繁可能导致处理队列阻塞,界面渲染不及时而导致卡顿,应避免无用的频繁调用。

由于setData的限制,为了性能优化,在项目中需要合理使用 setData

分页加载实现

问题: setData单次设置的数据不能超过1024kB,当数据量过大时,如何通过setData实现目的呢?

分析: 通过分批对同一个data中的对象进行修改,而不是覆盖某个data的对象。

常规分页加载

一次性将所有的list数据全部通过setData提交,分页后通过concat来拼接数组,再一次性setData。

代码实现:

index.wxml

<scroll-view class="container" scroll-y="{{true}}" bindscrolltolower="loadMore">
  <view class="announce-wrapper" wx:if="{{list.length}}">
    <view class="announce-card" wx:for="{{list}}" wx:key="index" data-id="{{item.id}}" bindtap="goDetail">
      <view class="announce-card-title">{{item.title}}</view>
      <view class="announce-card-date">{{item.create_time}}</view>
      <view class="announce-right">
      <image src="../../assets/right.png"></image>
      </view>
    </view>
    <view class='loadMore' wx:if="{{loading}}">{{text}}</view>
  </view>
  <view wx:else class="nodata">暂无公告</view>
</scroll-view>

index.js

Page({

  /**
   * 页面的初始数据
   */
  data: {
    list: [],
    page: 1,
    size: 20,
    loading: false,
    text: '上拉加载更多'
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onShow: function() {
    this.initData()
  },
  /**
   * 初始化数据
   */
  initData() {
    this.setData({
      page: 1
    })
    this._getData()
  },
  // 接口数据
  _getData() {
    const data = {
      page: this.data.page,
      size: this.data.size
    }
    getInfoList(data).then(res => {
      const contentList = res.list
      if (contentList.length) {
        if (this.data.page == 1) {
          this.setData({
            list: []
          })
        }
        // 判断最后一页
        if (res.pageNum == res.pages) {
          this.setData({
            list: this.data.list.concat(contentList),
            loading: false
          })
        } else {
          this.setData({
            list: this.data.list.concat(contentList),
            loading: true
          })
        }
      } else {
        wx.showToast({
          title: '无数据',
          icon: 'none'
        })
      }
    })
  },
  // 加载更多
  loadMore() {
    if (this.data.loading) {
      this.setData({
        page: this.data.page + 1
      })
      this._getData()
    }
  }
})

这种方法在数据量少时适用,一旦数据量过大就会出现页面卡顿和空白。

优化后的分页加载

将listArr列表数据分组后存放在页面中,需要数据时,提交指定listArr中指定位置的数据,只需要加载本次需要的数据。

优化的重点依据就是: setData函数支持改变数组中的某一项或对象的某个属性

list.js

Page({
  // 页面的初始数据
  data: {
    pageNum: 0,// 当前页数
    pageSize: 10,// // 每页数据条数
    listArr: [],// 列表二维数组
    total: 0, // 总条数
    pages: 0, // 总页数
  },

  // 生命周期函数--监听页面加载
  onLoad: function () {
    this.loadInitData();
  },
  // 加载初始页数据
  loadInitData() {
    // 刷新时,清空listArr,防止新数据与原数据冲突
    this.setData({
      listArr: [],
      pageNum: 1
    })

    this.initData()
  },
  // 初始化数据
  initData() {
    const { pageNum, pageSize } = this.data;
    const data = {pageNum:pageNum,pageSize:pageSize};
    getData(data).then(res => {
        this.setData({
            total: res.data.total,
            pages: res.data.pages,
            [`listArr[${pageNum - 1}]`]: res.data.list // 动态修改数组某一项的值,需要加中括号
      })
    })
  },
  // 加载新数据
  loadMore: function(){
    let { pageNum } = this.data;
    pageNum += 1;
    this.setData({
      pageNum: pageNum
    })
    this.initData();
  },
  // 监听用户下拉动作
  onPullDownRefresh() {
     this.loadInitData()
  },
  // 页面上拉触底事件的处理函数
  onReachBottom() {
    const { pageNum, pages } = this.data
    if (pageNum < pages) {
      this.loadMore()
    } else {
      dd.showToast({
        content: '没有更多数据了',
      })
    }
  },
})

list.wxml

 <view class="coupon-list" a:if="{{listArr.length>0}}">
      <scroll-view>
        <block a:for="{{listArr}}" a:key="index" a:for-item="caption">
          <view a:if="{{caption.length}}">
            <block a:for="{{caption}}" a:key="index">
              <coupon-card item="{{item}}" type="{{currentIndex}}" onClick="goDetail" onCancel="cancelApprove" onApprove="handleApprove"></coupon-card>
            </block>
          </view>
          <va-empty a:else text="暂无数据" />
        </block>
      </scroll-view>
    </view>
    <va-empty a:else text="暂无数据" />

这种方法在钉钉小程序中同样可以使用。