<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
長列表我們經常接觸到,長列表為什麼需要懶載入呢,因為一旦渲染內容多了,渲染引擎就需要更多的時間去渲染畫面,這時可能會出現頁面白屏、卡頓等。而使用者其實只需要看到視窗內的內容就可以了,不用一次性把全部內容渲染完,所以可以通過懶載入實現。
常見的列表懶載入是和後端一起實現,也就是分頁載入。前端請求第幾頁的資料,後端就返回第幾頁的資料。前端要實現的互動就是當用戶滑動到頁面底部時,就要請求下一頁的資料。
高度示意圖
監聽scroll事件,事件回撥會有當前元素的捲動距離scrollTop,當scrollTop+screenHeight等於捲動高度scrollHeight時,表示已經滑動到底部。
在小程式中,Page物件提供onReachBottomapi
onReachBottom: function() { // 頁面觸底時執行 },
用捲動監聽會非常耗效能,捲動時頻繁觸發回撥的,所以會不斷去計算判斷。比較好的優化方案是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資料量,又減少渲染時間
有很多場景用不了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就可以了。所以可以把懶載入的這塊邏輯抽離出來封裝。
到此這篇關於小程式列表懶載入的文章就介紹到這了,更多相關小程式列表懶載入內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45