每日 30 秒 ⏱ 對海量資料進行切割

zhangxiangliang發表於2019-03-14

簡介

資料分割、分頁、非同步操作、DOM優化

把陣列按指定大小進行分組,可以用於分頁、資料切割、非同步運算元據。

// 該原始碼來自於 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"
}

複製程式碼

一起成長

在困惑的城市裡總少不了並肩同行的 夥伴 讓我們一起成長。

  • 如果您想讓更多人看到文章可以點個 點贊
  • 如果您想激勵小二可以到 Github 給個 小星星
  • 如果您想與小二更多交流新增微信 m353839115

微信公眾號

本文原稿來自 PushMeTop

相關文章