每日 30 秒之 對海量資料進行切割
把陣列按指定大小進行分組,可以用於分頁、資料切割、非同步操作資料。
// 該原始碼來自於 https://30secondsofcode.org const chunk = (arr, size) => Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => arr.slice(i * size, i * size + size) ); 複製程式碼
程式碼分析
Array.prototype.from
從一個類似陣列或者可迭代物件中建立一個新的陣列例項,類似陣列
這個詞可能很多人都不是很清楚,類似陣列是javascript
中一個神奇的物件,只要擁有length
就算是類似陣列了。
最常見的類似陣列
是函式中的arguments
有長度和 arguments[0] 的呼叫方法,但是卻沒有陣列的 push 等函式方法。利用Array.prototype.from
則可以把類似陣列
轉化為陣列
。這個程式碼的巧妙之處在於用了{ length: 3 }
這樣的物件來快速生成陣列
,而Array.prototype.from
的第二個引數會對剛生成的陣列進行迴圈遍歷相當於呼叫了map
。
在迴圈遍歷新生成陣列
時,使用了Array.prototype.slice
的方法來實現了分割資料的效果,這個方法相當常用同學們可以記住它。
使用場景
假設現在有一個訊息列表數組裡面有一萬條資料讓你渲染到頁面上,大部分人會直接遍歷陣列並拼接成dom
一股腦的渲染到頁面上,這樣帶來的後果是大量的 dom 操作會花費很多時間導致頁面卡頓,且上下滑動頁面時也會卡頓。
我們不妨換個角度來看這個問題無論是手機螢幕還是電腦螢幕
使用者可見的頁面資料條目可能就十幾條。那為什麼我們要一次性渲染一萬多條,而且使用者也不見得會把所有資料都查看了。
那我們是否可以只渲染十幾條資料
,其他資料等使用者滾動了某個高度時再進行下一個十幾條資料
的渲染。在分頁操作中,chunk
就可以幫助我們快速的進行分頁。
樣式
.news > div { text-align: center; height: 50px; } 複製程式碼
結構
<!-- 用於標識到頁面頂部了 --> <div class="news-header"></div> <!-- 新聞資料 --> <div class="news"></div> <!-- 用於標識到頁面底部了 --> <div class="news-footer"></div> 複製程式碼
指令碼
// 模擬生成 1萬條資料,這裡就利用了 Array.from 來快速生成資料 const originNews = Array.from( { length: 10000 }, (v, k) => ({ content: `新聞${k}` }) ) // 需要插入的容器 const element = document.querySelector('.news')[0] // 建立檢視監聽 const loadObserver = new IntersectionObserver((entries) => { // 如果不可見,就返回 if (entries[0].intersectionRatio <= 0) { return; } // 判斷是否有上一頁和下一頁 const hasPrePage = page != 0 const hasNextPage = page != news.length - 1 const now = news[page] const pre = hasPrePage ? news[page - 1] : [] const next = hasNextPage ? news[page + 1] : [] // 傳遞錨點的座標 和 當前頁面顯示的資料 render(pre.length, [ ...pre, ...now, ...next ]) // 判斷是否需要翻頁,且防止陣列越界 page = entries[0].target.className == 'news-footer' || page === 0 ? (hasNextPage ? page + 1 : page) : (hasPrePage ? page - 1 : page) }, { threshold: [1] }) // 設定監聽 loadObserver.observe(document.querySelector('.news-header')) loadObserver.observe(document.querySelector('.news-footer')) // 根據當前頁面高度和新聞高度算出每一頁可以放幾條資料 let pageNum = Math.ceil(document.body.clientHeight / 50) let page = 0 // 當前顯示了第幾頁的資料 let news = chunk(originNews, pageNum) // 分頁後的資料 // 渲染新聞 並 跳轉到錨點 function render(last, data) { element.innerHTML = '' data.forEach((i, v) => element.innerHTML += v == last ? `<div id="news-herf">${i.content}</div>` : `<div>${i.content}</div>` ) window.location.href = "#news-herf" } 複製程式碼