preface

Most small programs will have such requirements, the page has a long list, need to pull down to the end of the request for background data, rendering data, when the data list is long, it will be found that the obvious card, the page white screen flash phenomenon.

Analysis of the

  • Requests for background data require constant setData and data consolidation, resulting in large data rendering in the later stage
  • There are many DOM structures to render, and DOM comparison will be carried out for each rendering, and the related DIff algorithm is time-consuming
  • The number of DOM is large, which occupies a large amount of memory, causing the page to be stuck on a blank screen

Initial render method

/* * @Descripttion: * @version: * @Author: shijuwang * @Date: 2021-07-14 16:40:22 * @LastEditors: shijuwang * @LastEditTime: 2021-07-14 17:10:13 */
//Page Object
Page({
  data: {
    list: [].// All data
  },
  //options(Object)
  onLoad: function (options) {
    this.index = 0
    const arr = [
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },




    ]
    this.setData({ list: arr })
  },

  onReachBottom: function () {
    const arr = [
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },

    ]
    let { list } = this.data
    this.setData({
      list: [...list, ...arr]
    })
  },

});


// wxml 
<view class="container">
    <view class="item-list" wx:for="{{list}}">
      <text class="">
          {{item.idx}}
      </text>
    </view>
</view>
Copy the code

Each time the data is requested again, the array is merged and the data is set again. When the list is taken care of, there will be a blank screen. Each time the data of setData is bigger and bigger, which increases the communication time and renders too many DOM elements, as shown in the figure below:

Preliminary optimization

1. Change the one-dimensional array to a two-dimensional array
  • Normally, setData is all data rerendered
  • Paging requests, which place relatively little pressure on the server, and paging incremental rendering puts less pressure on setData
  • SetData looks for the subindex of the data corresponding to the index of the two-dimensional array, and uses setData to carry out local refresh
  • When you setData, you just assign a value to the current screen
// wxml
<view class="container">
  <block wx:for="{{list}}" wx:for-index="pageNuma">
    <view class="item-list" wx:for="{{item}}">
      <text class="">{{item.idx}}</text>
    </view>
  </block>
</view>
// wx.js
Page({
  data: {
    list: [].// All data
  },
  //options(Object)
  onLoad: function (options) {
    this.index = 0;
    this.currentIndex = 0;          // The current page number is pageNuma
    const arr = [
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },




    ]
    this.setData({ [`list[The ${this.currentIndex}] `]: arr })
  },

  onReachBottom: function () {
    this.currentIndex++;            / / a + 1 bonus to hit bottom
    const arr = [
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },
      {
        idx: this.index++
      },

    ]
    this.setData({
      [`list[The ${this.currentIndex}] `]: arr
    })
  },

});
Copy the code

This way we can see that the entire render is rendered on screens as paging, and request several pagenums, so render several screens, and render every list in every screen.

Further optimization

2. We can render only one screen of the visual area or several screens near the visual area, and use view to occupy other areas without specific rendering, thus reducing DOM rendering and problems caused by too many nodes.

To do this, there are several steps:

  • Gets the current screen height, the height per screen when rendering, and the scrolling distance calculation
  • Stores all rendering data retrieved, stores the height of each screen, and calculates which screen the viewable area is on via onPageScroll
  • Other than the visible area data is empty, use view placeholder
is.currentIndex = 0; PageNum this.pageHeight = []; This. allList = []; This. systemHeight = 0; this.systemHeight = 0; This.visualindex = []; // Store the visual area pageNumCopy the code

When the page is displayed, mount the relevant data:

// wxml <view wx:for="{{list}}" wx:for-index="pageNum" id="item{{pageNum}}"> <view class="item-list" wx:for="{{item}}"> <text class="">{{item.idx}}</text> </view> </view> onLoad: function (options) { this.index = 0; this.currentIndex = 0; PageNum this.pageHeight = []; This. allList = []; This. systemHeight = 0; this.systemHeight = 0; / / screen height const arr = [{independence idx: enclosing index++}, {independence idx: enclosing index++}, {independence idx: enclosing index++}, {independence idx: this.index++ }, { idx: this.index++ } ] this.setData({ [`list[${this.currentIndex}]`]: arr }, () => { this.setPageHeight(); }); this.getSystemInfo(); },Copy the code

Dynamically set the ID name for each screen view, which is convenient to obtain the height of each screen during rendering, and store it, and set the height for the subsequent non-visible area

Loop pageHeight, add each pageHeight and the current scrolling distance to compare, get the pageNum of the current rendering, and then the current selection of rendering pageNum -+1, the last screen and the next screen into the array, the current three page data display, other screen data for view placeholder, height for storage height.

OnPageScroll: throttle(function (e) {let pageScrollTop = e[0].scrollTop; let that = this; // Let scrollTop = 0; let currentIndex = this.currentIndex; for (var i = 0; i < this.pageHeight.length; i++) { scrollTop = scrollTop + this.pageHeight[i]; if (scrollTop > pageScrollTop + this.systemHeight - 50) { this.currentIndex = i; this.visualIndex = [i - 1, i, i + 1]; that.setData({ visualIndex: this.visualIndex }) break; }}}, 200).Copy the code
Real-time monitoring of rolling, do throttling optimization processing
const throttle = (fn, interval) => { var enterTime = 0; / var/trigger time gapTime = interval | | 300; Return function () {var that = this;} return function () {var that = this; var backTime = new Date(); If (backtime-entertime > gapTime) {fn.call(that, arguments); enterTime = backTime; // The value assigned to the first trigger saves the second trigger time}}; }Copy the code

If the current pageNum is in the visualIndex, render it, if not, view is occupied, and height is stored

<wxs module='filter'> var includesList = function(list,currentIndex){ if(list){ return list.indexOf(currentIndex) > -1 }  } module.exports.includesList = includesList; </wxs> <view class="container"> <view wx:for="{{list}}" wx:for-index="pageNum" id="item{{pageNum}}" wx:key="pageNum"> <block wx:if="{{filter.includesList(visualIndex,pageNum)}}"> <view class="item-list" wx:for="{{item}}"> <text class="">{{item.idx}}</text> </view> </block> <block wx:else> <view class="item-visible" style="height:{{pageHeight[pageNum]}}px"></view> </block> </view> </view>Copy the code

The pageNum of the array in the visible area loops through the current array, and if not sets the height placeholder, rendering the result as follows:

And you’re done!

Method 2

Use the wechat applet API

IntersectionObserver

// observePage: function (pageNum) {const that = this; const observerView = wx.createIntersectionObserver(this).relativeToViewport({ top: 0, bottom: 0}); observerView.observe(`#item${pageNum}`, (res) => { console.log(res,'res'); if (res.intersectionRatio > 0) { that.setData({ visualIndex: [pageNum - 1, pageNum, pageNum + 1] }) } }) }Copy the code

Oberserve is used to listen to whether the current page is in the visual area, if yes, put the current pageNum -+1, pageNum into the visual area array visualIndex, in WXS to determine whether the current pageNum exists in the visual area array, if it is rendered, if not, use view space.

Scheme a scrolling calculation >>> code snippet:(Scroll virtual list)

Solution 2 IntersectionObserver API >>>intersectionObserver

Code word is not easy, I hope the big guy Pointers!