亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

小程序列表懶加載如何實現

發布時間:2022-04-02 09:14:16 來源:億速云 閱讀:523 作者:iii 欄目:開發技術

本文小編為大家詳細介紹“小程序列表懶加載如何實現”,內容詳細,步驟清晰,細節處理妥當,希望這篇“小程序列表懶加載如何實現”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

    小程序上的列表懶加載

    長列表我們經常接觸到,長列表為什么需要懶加載呢,因為一旦渲染內容多了,渲染引擎就需要更多的時間去渲染畫面,這時可能會出現頁面白屏、卡頓等。而用戶其實只需要看到視窗內的內容就可以了,不用一次性把全部內容渲染完,所以可以通過懶加載實現。

    分頁加載

    常見的列表懶加載是和后端一起實現,也就是分頁加載。前端請求第幾頁的數據,后端就返回第幾頁的數據。前端要實現的交互就是當用戶滑動到頁面底部時,就要請求下一頁的數據。

    用scroll事件監聽

    小程序列表懶加載如何實現

    高度示意圖

    監聽scroll事件,事件回調會有當前元素的滾動距離scrollTop,當scrollTop+screenHeight等于滾動高度scrollHeight時,表示已經滑動到底部。

    在小程序中,Page對象提供onReachBottomapi

    onReachBottom: function() {
      // 頁面觸底時執行
    },

    用IntersectionObserver監聽

    用滾動監聽會非常耗性能,滾動時頻繁觸發回調的,所以會不斷去計算判斷。比較好的優化方案是IntersectionObserverAPI,當IntersectionObserver監聽的元素與可視區有相交狀態時,就會產生回調,這樣就減少了觸發的頻率

    Page({
      onLoad: function(){
        wx.createIntersectionObserver().relativeToViewport({bottom: 100}).observe('.target-class', (res) => {
          res.intersectionRatio // 相交區域占目標節點的布局區域的比例,不等于0時表示有相交
          res.intersectionRect // 相交區域
          res.intersectionRect.left // 相交區域的左邊界坐標
          res.intersectionRect.top // 相交區域的上邊界坐標
          res.intersectionRect.width // 相交區域的寬度
          res.intersectionRect.height // 相交區域的高度
        })
      }
    })

    前端分頁渲染

    上面說的都是前端結合接口的分頁加載。假如說接口沒有分頁,直接就返回了龐大的數據列表。前端如果直接就setData所有數據,會渲染很久,其實復雜的列表渲染20條的時候就已經很慢了。這個時候需要對已經獲取到的數據進行分頁,分批進行渲染。

    小程序列表懶加載如何實現

    通過右側面板可以看到,一開始沒有渲染所有節點,在滑動頁面過程中節點再不斷增加。

    直接上代碼

    <!-- pages/first/index.wxml -->
    <view class="container">
      <block wx:for="{{goodsList}}" wx:key="index" wx:for-item="subItemList">
        <block wx:for="{{subItemList}}" wx:key="name">
          <view class="item">{{item.name}}</view>
        </block>
      </block>
    </view>

    goodsList是一個二維數組,用wx:for雙重遍歷

    // pages/first/index.js
    const list = Array.from({length: 5}, (item, index) => {
      return Array.from({length: Math.ceil(Math.random()*10 + 5)}, (subItem, subIndex) => {
        return {name: `第${index+1}屏, 第${subIndex+1}個`}
      })
    })
    /**
    生成的list是一個二維數組
    [
      [{}, {}],
      [{}, {}],
      [{}, {}],
      ...
    ]
    **/
    
    Page({
      data: {
        goodsList: [],
        lazyloadIdx: 0
      },
      onLoad() {
        this.setData({
          goodsList: [list[0]],
          lazyloadIdx: 1
        })
      },
      // 滑動到底部時往goodsList添加數據
      onReachBottom () {
        console.log('onReachBottom');
        let { lazyloadIdx } = this.data
        if (lazyloadIdx >= list.length) return
        this.setData({
          [`goodsList[${lazyloadIdx}]`]: list[lazyloadIdx],
          lazyloadIdx: lazyloadIdx+1
        })
      }
    })

    每次只setData一屏數據,既減少了setData數據量,又減少渲染時間

    用IntersectionObserver代替onReachBottom

    有很多場景用不了onReachBottom,那我們只能用IntersectionObserver代替。優化一下上面的代碼

    # pages/second/index.wxml
    <view class="container">
      <block wx:for="{{goodsList}}" wx:key="index" wx:for-item="subItemList">
        <block wx:for="{{subItemList}}" wx:key="name">
          <view class="item">{{item.name}}</view>
        </block>
      </block>
    +  <view id="observer" class="bottom"></view>
    </view>

    增加節點用來監聽

    //  pages/second/index.js
    let lazyloadOb = null
    Page({
      data: {
        goodsList: [],
        lazyloadIdx: 0
      },
      onLoad() {
        this.setData({
          goodsList: [list[0]],
          lazyloadIdx: 1
        })
        this.initObserver()
      },
      onunload () {
        this.disconnenct()
      },
      lazyloadNext () {
        console.log('lazyloadNext');
        let { lazyloadIdx } = this.data
        if (lazyloadIdx >= list.length) return
        this.setData({
          [`goodsList[${lazyloadIdx}]`]: list[lazyloadIdx],
          lazyloadIdx: lazyloadIdx+1
        })
      },
      initObserver () {
        lazyloadOb = wx.createIntersectionObserver().relativeToViewport({ bottom: 50 }).observe('#observer', (res) => {
          console.log('res.intersectionRatio', res.intersectionRatio);
          // 觸發回調時加載下一屏
          if (res.intersectionRatio) this.lazyloadNext()
        })
      },
      disconnenct() {
        lazyloadOb.disconnenct()
      }
    })

    加需求!

    后端返回的商品列表只是一個一維數組,需要前端轉為二維數組,現在需要每屏的列表長度為5。

    假設商品列表個數為21,那么會生成二維數組對應的子項長度:

    // 偽代碼
    [5, 5, 5, 5, 1]

    我們可以設定分頁大小pageSize為5,當前分頁pageNum,然后list.slice(pageNum, pageSize)就能截取對應的數據,再加入到二維數組中。

    但是產品來加需求了,商品列表默認只展示非售罄商品+一個售罄商品,其余售罄商品要點擊【查看更多】按鈕才展示

    假設非售罄商品有16個,售罄11個,每屏的列表長度還是5,那么:

    [
      5, 5, 5,    // 非售罄
      2,          // 非售罄+售罄
      5, 5        // 售罄
    ]

    只有兩個的長度不大適合再分一屏,所以小于5時,直接跟前面的合并

    [
      5, 5, 7, // 非售罄+一個售罄
      5, 5     // 售罄
    ]

    這個時候設定pageSize就沒法滿足了,所以要根據售罄個數,非售罄個數以及一屏長度,算出長度數組,再算出對應的二維數組

    /**
      * @desc 生成商品列表的子項長度
      * 展示列表包含售罄的,在非售罄列表最后拼接一個售罄商品,點擊展開再加載售罄
      * 
      * @param {number} onSaleLen 非售罄長度
      * @param {number} soldOutLen 售罄長度
      * @returns { { subSize: Array<number>; soldOutLen: number } }
      */
    genSubListSize (onSaleLen, soldOutLen) {
      // 有售罄的時候,放一項售罄到非售罄那邊去
      if (soldOutLen) {
        soldOutLen-= 1
        onSaleLen+=1
      }
      const arr = []
      const lazyloadListPartLength = 5 // 一屏5個
      let firstSize = 0
      if (onSaleLen < lazyloadListPartLength*2) {
        firstSize = onSaleLen
        onSaleLen = 0
      } else {
        firstSize = lazyloadListPartLength
        onSaleLen -= lazyloadListPartLength
      }
      arr.push(firstSize)
      
      // 子項長度
      const size = lazyloadListPartLength
      const remainder = onSaleLen % size
      arr.push(
        ...Array.from({length: Math.floor(onSaleLen/size) - 1}, () => size),
      )
      if (onSaleLen) {
        arr.push(onSaleLen <= size ? onSaleLen : size + remainder)
      }
      // 記錄此時售罄項的索引,因為要點擊展開才能加載售罄列表
      const soldOutIndex = arr.length
      if (soldOutLen) {
        const remainder = soldOutLen % size
        arr.push(
          ...Array.from({length: Math.floor(soldOutLen/size) - 1}, () => size), 
          soldOutLen <= size ? soldOutLen : size + remainder
        )
      }
    
      console.log('genSubListSize', arr)
      
      return {
        subSize: arr,
        soldOutLen,
        soldOutIndex
      }
    }
    /**
      * eg: onSaleLen = 25; soldOutLen = 9; size = 5
      * return [5, 5, 5, 5, 6, 8]
      * eg: onSaleLen = 15; soldOutLen = 9; size = 5
      * return [5, 5, 6, 8]
      * eg: onSaleLen = 10; soldOutLen = 10; size = 5
      * return [5, 6, 9]
      * eg: onSaleLen = 14; soldOutLen = 10; size = 5
      * return [5, 5, 5, 9]
      * eg: onSaleLen = 8; soldOutLen = 9; size = 5
      * return [9, 8]
      * eg: onSaleLen = 2; soldOutLen = 10; size = 7 像這種小于非售罄小于size的,只能取到3了
      * return [3, 9]
    **/

    現在取列表長度為20,12個非售罄,8個售罄,一屏5個

    // pages/third/index
    const goodsList = Array.from({length: 20}, (item, index) => {
      return {name: `第${index+1}個`, soldOut: index < 12 ? false : true}
    })
    Page({
      // ...
      onLoad() {
        this.initObserver()
        this.handleGoodsList()
      },
      handleGoodsList () {
        const { onSaleLen, soldOutLen } = this.countSaleLen()
        console.log('onSaleLen', onSaleLen, 'soldOutLen', soldOutLen);
        const {
          subSize,
          soldOutLen: soldOutLength,
          soldOutIndex
        } = this.genSubListSize(onSaleLen, soldOutLen)
        const renderList = this.genRenderList(subSize)
        console.log('renderList', renderList);
      },
      countSaleLen () {
        const soldOutIndex = goodsList.findIndex(good => good.soldOut)
        if (soldOutIndex === -1) {
          return {
            onSaleLen: goodsList.length,
            soldOutLen: 0
          }
        }
        return {
          onSaleLen: soldOutIndex,
          soldOutLen: goodsList.length - soldOutIndex
        }
      },
      // 根據分組數組生成渲染列表
      genRenderList (subSize) {
        const before = goodsList
        const after = []
        let subList = [] // 二維數組子項
        let subLen = 0 // 子項長度
        let splitSizeArrIdx = 0 // 長度數組索引
        for (let i = 0; i < before.length; i++) {
          if (subLen === subSize[splitSizeArrIdx]) {
            splitSizeArrIdx++
            after.push(subList)
            subList = []
            subLen = 0
          }
          before[i]['part'] = `第${splitSizeArrIdx+1}屏`
          subList.push(before[i])
          subLen++
        }
        // 最后一個子項添加進去
        after.push(subList)
        return after
      }
    })

    打印一下renderList,得到了動態切分的數據了

    小程序列表懶加載如何實現

    列表分組

    跑一下demo

    小程序列表懶加載如何實現

    當然需求是不斷變化的,下次就不一定是售罄非售罄了,但是萬變不離其宗,再怎么分,把每一項的數組長度定好之后,再生成渲染的renderList就可以了。所以可以把懶加載的這塊邏輯抽離出來封裝。

    讀到這里,這篇“小程序列表懶加載如何實現”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    海淀区| 长武县| 章丘市| 锦屏县| 灯塔市| 漯河市| 曲阳县| 襄樊市| 山丹县| 镇原县| 张家口市| 永州市| 宜兰市| 兴国县| 西安市| 麦盖提县| 汪清县| 呼伦贝尔市| 饶平县| 钦州市| 泗水县| 砀山县| 绥棱县| 盘山县| 海兴县| 定日县| 岳阳县| 丰镇市| 屯留县| 古浪县| 乐安县| 伊通| 仪陇县| 安新县| 沅陵县| 松原市| 绿春县| 开鲁县| 凯里市| 隆林| 灵丘县|