欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

小程序列表懶加載的實現(xiàn)方式

 更新時間:2022年04月01日 15:57:11   作者:酒闌人散  
懶加載,前端人都知道的一種性能優(yōu)化方式,簡單的來說,只有當圖片出現(xiàn)在瀏覽器的可視區(qū)域內(nèi)時,才設置圖片正真的路徑,讓圖片顯示出來,這篇文章主要給大家介紹了關于小程序列表懶加載的實現(xiàn)方式,需要的朋友可以參考下

小程序上的列表懶加載

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

分頁加載

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

用scroll事件監(jiān)聽

高度示意圖

監(jiān)聽scroll事件,事件回調(diào)會有當前元素的滾動距離scrollTop,當scrollTop+screenHeight等于滾動高度scrollHeight時,表示已經(jīng)滑動到底部。

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

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

用IntersectionObserver監(jiān)聽

用滾動監(jiān)聽會非常耗性能,滾動時頻繁觸發(fā)回調(diào)的,所以會不斷去計算判斷。比較好的優(yōu)化方案是IntersectionObserverAPI,當IntersectionObserver監(jiān)聽的元素與可視區(qū)有相交狀態(tài)時,就會產(chǎn)生回調(diào),這樣就減少了觸發(fā)的頻率

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

前端分頁渲染

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

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

直接上代碼

<!-- 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是一個二維數(shù)組,用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是一個二維數(shù)組
[
  [{}, {}],
  [{}, {}],
  [{}, {}],
  ...
]
**/

Page({
  data: {
    goodsList: [],
    lazyloadIdx: 0
  },
  onLoad() {
    this.setData({
      goodsList: [list[0]],
      lazyloadIdx: 1
    })
  },
  // 滑動到底部時往goodsList添加數(shù)據(jù)
  onReachBottom () {
    console.log('onReachBottom');
    let { lazyloadIdx } = this.data
    if (lazyloadIdx >= list.length) return
    this.setData({
      [`goodsList[${lazyloadIdx}]`]: list[lazyloadIdx],
      lazyloadIdx: lazyloadIdx+1
    })
  }
})

每次只setData一屏數(shù)據(jù),既減少了setData數(shù)據(jù)量,又減少渲染時間

用IntersectionObserver代替onReachBottom

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

# 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>

增加節(jié)點用來監(jiān)聽

//  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);
      // 觸發(fā)回調(diào)時加載下一屏
      if (res.intersectionRatio) this.lazyloadNext()
    })
  },
  disconnenct() {
    lazyloadOb.disconnenct()
  }
})

加需求!

后端返回的商品列表只是一個一維數(shù)組,需要前端轉(zhuǎn)為二維數(shù)組,現(xiàn)在需要每屏的列表長度為5。

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

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

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

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

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

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

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

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

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

/**
  * @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]
**/

現(xiàn)在取列表長度為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
    }
  },
  // 根據(jù)分組數(shù)組生成渲染列表
  genRenderList (subSize) {
    const before = goodsList
    const after = []
    let subList = [] // 二維數(shù)組子項
    let subLen = 0 // 子項長度
    let splitSizeArrIdx = 0 // 長度數(shù)組索引
    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,得到了動態(tài)切分的數(shù)據(jù)了

列表分組

跑一下demo

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

demo源碼

以上三個demo的完整代碼

參考

到此這篇關于小程序列表懶加載的文章就介紹到這了,更多相關小程序列表懶加載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • JS window對象的top、parent、opener含義介紹

    JS window對象的top、parent、opener含義介紹

    本文為大家介紹下JS window對象的top、parent、opener含義,不了解的朋友可以參考下,希望對大家有所幫助
    2013-12-12
  • uniapp中的picker選擇器的幾種使用場景

    uniapp中的picker選擇器的幾種使用場景

    本文主要介紹了uniapp中的picker選擇器的幾種使用場景,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-05-05
  • 深入淺析javascript立即執(zhí)行函數(shù)

    深入淺析javascript立即執(zhí)行函數(shù)

    在Javascript中,任何function在執(zhí)行的時候都會創(chuàng)建一個執(zhí)行上下文,因為為function聲明的變量和function有可能只在該function內(nèi)部,這個上下文,在調(diào)用function的時候,提供了一種簡單的方式來創(chuàng)建自由變量或私有子function。
    2015-10-10
  • JavaScript相等運算符的九條規(guī)則示例詳解

    JavaScript相等運算符的九條規(guī)則示例詳解

    這篇文章主要給大家介紹了關于JavaScript相等運算符的九條規(guī)則,文中通過示例代碼介紹的非常詳細,對大家的學習或者使用JavaScript具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-10-10
  • JS實現(xiàn)選項卡效果的代碼實例

    JS實現(xiàn)選項卡效果的代碼實例

    這篇文章主要介紹了JS選項卡效果,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-05-05
  • JS條形碼(一維碼)插件JsBarcode用法詳解【編碼類型、參數(shù)、屬性】

    JS條形碼(一維碼)插件JsBarcode用法詳解【編碼類型、參數(shù)、屬性】

    這篇文章主要介紹了JS條形碼(一維碼)插件JsBarcode用法,較為詳細的分析了條形碼插件JsBarcode編碼類型、參數(shù)、屬性等相關功能、使用方法與注意事項,需要的朋友可以參考下
    2017-04-04
  • 跟我學Nodejs(三)--- Node.js模塊

    跟我學Nodejs(三)--- Node.js模塊

    這是本系列的第三篇文章了,前面2篇網(wǎng)友們反饋回來不少的消息,加上最近2天比較忙,一直沒來得及整理,周末了,趕緊給大家整理下發(fā)出來,本文講的是node.js模塊
    2014-05-05
  • JS實現(xiàn)pasteHTML兼容ie,firefox,chrome的方法

    JS實現(xiàn)pasteHTML兼容ie,firefox,chrome的方法

    這篇文章主要介紹了JS實現(xiàn)pasteHTML兼容ie,firefox,chrome的方法,涉及javascript針對頁面元素的動態(tài)操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2016-06-06
  • AngularJs 禁止模板緩存的方法

    AngularJs 禁止模板緩存的方法

    本篇文章主要介紹了AngularJs 禁止模板緩存的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • javascript刪除Table中的一行的腳本代碼

    javascript刪除Table中的一行的腳本代碼

    用js實現(xiàn)的刪除table中一行數(shù)據(jù)的代碼
    2008-06-06

最新評論