微信小程序 擴展組件·小程序長列表組件

2022-05-12 17:45 更新

recycle-view

小程序長列表組件

使用此組件需要依賴小程序基礎(chǔ)庫 2.2.2 版本,同時依賴開發(fā)者工具的 npm 構(gòu)建。具體詳情可查閱官方 npm 文檔。

背景

?目前小程序會有不少的應(yīng)用場景里會用到無限長列表的交互,當一個頁面展示很多信息的時候,會造成小程序頁面的卡頓以及白屏。原因有如下幾點:

  1. 列表數(shù)據(jù)很大,首次 setData 的時候耗時高
  2. 渲染出來的列表 DOM 結(jié)構(gòu)多,每次 setData 都需要創(chuàng)建新的虛擬樹、和舊樹 diff 操作耗時都比較高
  3. 渲染出來的列表 DOM 結(jié)構(gòu)多,占用的內(nèi)存高,造成頁面被系統(tǒng)回收的概率變大。

因此就有長列表組件來解決這些問題。

實現(xiàn)思路

?核心的思路是只渲染顯示在屏幕的數(shù)據(jù),基本實現(xiàn)就是監(jiān)聽 scroll 事件,并且重新計算需要渲染的數(shù)據(jù),不需要渲染的數(shù)據(jù)留一個空的 div 占位元素。

假設(shè)列表數(shù)據(jù)有100個 item,知道了滾動的位置,怎么知道哪些 item 必須顯示在頁面?因為 item 還沒渲染出來,不能通過 getComputedStyle 等 DOM 操作得到每個 item 的位置,所以無法知道哪些 item 需要渲染。為了解決這個問題,需要每個 item 固定寬高。item 的寬高的定義見下面的 API 的createRecycleContext()的參數(shù) itemSize 的介紹。

滾動過程中,重新渲染數(shù)據(jù)的同時,需要設(shè)置當前數(shù)據(jù)的前后的 div 占位元素高度,同時是指在同一個渲染周期內(nèi)。頁面渲染是通過 setData 觸發(fā)的,列表數(shù)據(jù)和 div 占位高度在2個組件內(nèi)進行 setData 的,為了把這2個 setData 放在同一個渲染周期,用了一個 hack 方法,所以定義 recycle-view 的 batch 屬性固定為 batch="{{batchSetRecycleData}}"。

在滾動過程中,為了避免頻繁出現(xiàn)白屏,會多渲染當前屏幕的前后2個屏幕的內(nèi)容。

包結(jié)構(gòu)

長列表組件由2個自定義組件 recycle-view、recycle-item 和一組 API 組成,對應(yīng)的代碼結(jié)構(gòu)如下

├── miniprogram-recycle-view/
    └── recycle-view 組件
    └── recycle-item 組件
    └── index.js

包結(jié)構(gòu)詳細描述如下:

目錄/文件 描述
recycle-view 組件 長列表組件
recycle-item 組件 長列表每一項 item 組件
index.js 提供操作長列表數(shù)據(jù)的API

使用方法:

1.安裝組件

npm install --save miniprogram-recycle-view

2.在頁面的 json 配置文件中添加 recycle-view 和 recycle-item 自定義組件的配置

{
  "usingComponents": {
    "recycle-view": "miniprogram-recycle-view/recycle-view",
    "recycle-item": "miniprogram-recycle-view/recycle-item"
  }
}

3.WXML 文件中引用 recycle-view

<recycle-view batch="{{batchSetRecycleData}}" id="recycleId">
  <view slot="before">長列表前面的內(nèi)容</view>
  <recycle-item wx:for="{{recycleList}}" wx:key="id">
    <view>
        <image style='width:80px;height:80px;float:left;' src="{{item.image_url}}"></image>
      {{item.idx+1}}. {{item.title}}
    </view>
  </recycle-item>
  <view slot="after">長列表后面的內(nèi)容</view>
</recycle-view>

recycle-view 的屬性介紹如下:

字段名類型必填描述
idStringid必須是頁面唯一的字符串
batchBoolean必須設(shè)置為{{batchSetRecycleData}}才能生效
heightNumber設(shè)置recycle-view的高度,默認為頁面高度
widthNumber設(shè)置recycle-view的寬度,默認是頁面的寬度
enable-back-to-topBoolean默認為false,同scroll-view同名字段
scroll-topNumber默認為false,同scroll-view同名字段
scroll-yNumber默認為true,同scroll-view同名字段
scroll-to-indexNumber設(shè)置滾動到長列表的項
placeholder-imageString默認占位背景圖片,在渲染不及時的時候顯示,不建議使用大圖作為占位。建議傳入SVG的Base64格式,可使用工具將SVG代碼轉(zhuǎn)為Base64格式。支持SVG中設(shè)置rpx。
scroll-with-animationBoolean默認為false,同scroll-view的同名字段
lower-thresholdNumber默認為false,同scroll-view同名字段
upper-thresholdNumber默認為false,同scroll-view同名字段
bindscroll事件同scroll-view同名字段
bindscrolltolower事件同scroll-view同名字段
bindscrolltoupper事件同scroll-view同名字段

recycle-view 包含3個 slot,具體介紹如下:

名稱描述
before默認 slot 的前面的非回收區(qū)域
默認 slot長列表的列表展示區(qū)域,recycle-item 必須定義在默認 slot 中
after默認 slot 的后面的非回收區(qū)域

長列表的內(nèi)容實際是在一個 scroll-view 滾動區(qū)域里面的,當長列表里面的內(nèi)容,不止是單獨的一個列表的時候,例如我們頁面底部都會有一個 copyright 的聲明,我們就可以把這部分的內(nèi)容放在 before 和 after 這2個 slot 里面。

recycle-item 的介紹如下:

需要注意的是,recycle-item 中必須定義 wx:for 列表循環(huán),不應(yīng)該通過 setData 來設(shè)置 wx:for 綁定的變量,而是通過createRecycleContext方法創(chuàng)建RecycleContext對象來管理數(shù)據(jù),createRecycleContext在 index.js 文件里面定義。建議同時設(shè)置 wx:key,以提升列表的渲染性能。

4.頁面 JS 管理 recycle-view 的數(shù)據(jù)

const createRecycleContext = require('miniprogram-recycle-view')
Page({
    onReady: function() {
        var ctx = createRecycleContext({
          id: 'recycleId',
          dataKey: 'recycleList',
          page: this,
          itemSize: { // 這個參數(shù)也可以直接傳下面定義的this.itemSizeFunc函數(shù)
            width: 162,
            height: 182
          }
        })
        ctx.append(newList)
        // ctx.update(beginIndex, list)
        // ctx.destroy()
    },
    itemSizeFunc: function (item, idx) {
        return {
            width: 162,
            height: 182
        }
    }
})

頁面必須通過 Component 構(gòu)造器定義,頁面引入了miniprogram-recycle-view包之后,會在 wx 對象下面新增接口createRecycleContext函數(shù)創(chuàng)建RecycleContext對象來管理 recycle-view 定義的的數(shù)據(jù),createRecycleContext接收類型為1個 Object 的參數(shù),Object 參數(shù)的每一個 key 的介紹如下:

參數(shù)名類型描述
idString對應(yīng) recycle-view 的 id 屬性的值
dataKeyString對應(yīng) recycle-item 的 wx:for 屬性設(shè)置的綁定變量名
pagePage/Componentrecycle-view 所在的頁面或者組件的實例,頁面或者組件內(nèi)可以直接傳 this
itemSizeObject/Function此參數(shù)用來生成recycle-item的寬和高,前面提到過,要知道當前需要渲染哪些item,必須知道item的寬高才能進行計算
Object必須包含{width, height}兩個屬性,F(xiàn)unction的話接收item, index這2個參數(shù),返回一個包含{width, height}的Object
itemSize如果是函數(shù),函數(shù)里面this指向RecycleContext
如果樣式使用了rpx,可以通過transformRpx來轉(zhuǎn)化為px。
為Object類型的時候,還有另外一種用法,詳細情況見下面的itemSize章節(jié)的介紹。
useInPageBoolean是否整個頁面只有recycle-view。Page的定義里面必須至少加空的onPageScroll函數(shù),主要是用在頁面級別的長列表,并且需要用到onPullDownRefresh的效果。切必須設(shè)置root參數(shù)為當前頁面對象
rootPage當前頁面對象,可以通過getCurrentPages獲取, 當useInPage為true必須提供

RecycleContext 對象提供的方法有:

方法參數(shù)說明
appendlist, callback在當前的長列表數(shù)據(jù)上追加list數(shù)據(jù),callback是渲染完成的回調(diào)函數(shù)
splicebegin, count, list, callback插入/刪除長列表數(shù)據(jù),參數(shù)同Array的splice函數(shù),callback是渲染完成的回調(diào)函數(shù)
updatebegin, list, callback更新長列表的數(shù)據(jù),從索引參數(shù)begin開始,更新為參數(shù)list,參數(shù)callback同splice。
destroy銷毀RecycleContext對象,在recycle-view銷毀的時候調(diào)用此方法
forceUpdatecallback, reinitSlot重新渲染recycle-view。callback是渲染完成的回調(diào)函數(shù),當before和after這2個slot的高度發(fā)生變化時候調(diào)用此函數(shù),reinitSlot設(shè)置為true。當item的寬高發(fā)生變化的時候也需要調(diào)用此方法。
getBoundingClientRectindex獲取某個數(shù)據(jù)項的在長列表中的位置,返回{left, top, width, height}的Object。
getScrollTop獲取長列表的當前的滾動位置。
transformRpxrpx將rpx轉(zhuǎn)化為px,返回轉(zhuǎn)化后的px整數(shù)。itemSize返回的寬高單位是px,可以在這里調(diào)用此函數(shù)將rpx轉(zhuǎn)化為px,參數(shù)是Number,例如ctx.transformRpx(140),返回70。注意,transformRpx會進行四舍五入,所以transformRpx(20) + transformRpx(90)不一定等于transformRpx(110)
getViewportItemsinViewportPx獲取在視窗內(nèi)的數(shù)據(jù)項,用于判斷某個項是否出現(xiàn)在視窗內(nèi)。用于曝光數(shù)據(jù)上報,菜品和類別的聯(lián)動效果實現(xiàn)。參數(shù)inViewportPx表示距離屏幕多少像素為出現(xiàn)在屏幕內(nèi),可以為負值。

其中 itemSize 的使用

itemSize可以為包含{width, height}的Object,所有數(shù)據(jù)只有一種寬高信息。如果有多種,則可以提供一個函數(shù),長列表組件會調(diào)用這個函數(shù)生成每條數(shù)據(jù)的寬高信息,如下所示:

function(item, index) {
    return {
        width: 195,
        height: item.azFirst ? 130 : 120
    }
}

提示:

  1. recycle-view設(shè)置batch屬性的值必須為{{batchSetRecycleData}}。
  2. recycle-item的寬高必須和itemSize設(shè)置的寬高一致,否則會出現(xiàn)跳動的bug。
  3. recycle-view設(shè)置的高度必須和其style里面設(shè)置的樣式一致。
  4. createRecycleContext(options)的id參數(shù)必須和recycle-view的id屬性一致,dataKey參數(shù)必須和recycle-item的wx:for綁定的變量名一致。
  5. 不能在recycle-item里面使用wx:for的index變量作為索引值的,請使用{{item._index_}}替代。
  6. 不要通過setData設(shè)置recycle-item的wx:for的變量值,建議recycle-item設(shè)置wx:key屬性。
  7. 如果長列表里面包含圖片,必須保證圖片資源是有HTTP緩存的,否則在滾動過程中會發(fā)起很多的圖片請求。
  8. 有些數(shù)據(jù)不一定會渲染出來,使用wx.createSelectorQuery的時候有可能會失效,可使用RecycleContext的getBoundingClientRect來替代。
  9. 當使用了useInPage參數(shù)的時候,必須在Page里面定義onPageScroll事件。
  10. transformRpx會進行四舍五入,所以transformRpx(20) + transformRpx(90)不一定等于transformRpx(110)
  11. 如果一個頁面有多個長列表,必須多設(shè)置batch-key屬性,每個的batch-key的值和batch屬性的變量必須不一致。例如
<recycle-view batch="{{batchSetRecycleData}}" batch-key="batchSetRecycleData"></recycle-view>
<recycle-view batch="{{batchSetRecycleData1}}" batch-key="batchSetRecycleData1"></recycle-view>


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號