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.