今天跟大家聊一下關於大型資料的一些例子。先說在最前面的,這種案例其實不多,屬於 八奇 系列。

一般來說,我們在 API 能拿到的 JSON 你覺得該有多大?


大量資料的渲染問題

實際上我拿過最大的 JSON 大概是 20MB,這是什麼概念呢?想像一下你需要在前端一口氣渲染超過 2000 個 DOM 元件,在這種情況下,如果你的前端裝置效能不好的話,那麼你可能就會卡在畫面上等了好久才會有東西出現。

對於 JavaScript 來說,單純的使用 for 迴圈,從 1 數到 2000 的話,你覺得大概需要多久?以我的 2012 年的 Macbook Pro 用 Chrome 來跑,單純的插入一個純文字的 <p>,大概是 65 ms 上下。這是以純文字的方式來處理,所得到的數字。

今天,當你的對象是比較複雜的 DOM 的話,那麼這個數字就可能會來到相當可怕的等級。舉例來說,我一樣是渲染 2000 個 DOM,然後將每個 DOM 都綁上了一個 click 事件,那麼,在 Vue 裡面,他的渲染過程就會延遲到 3 ~ 5 秒的等級。以我的電腦來說,大概被放大了 5 ~ 10 倍左右。


所以,面對這種比較奇怪的需求,我們該怎麼處理?通常來說,有幾種解法:

  • 不要用框架。
  • 用 Canvas 處理畫面。
  • 用 Vanilla JS 處理事件。

以單一事件的綁定需求來說,如果同時要在 2000 個元件綁定一個事件,那麼我們可以換個角度來思考,可能我們比較原始的作法是這樣:

<div id="container">
    <span @click.prevent="doSomething($event)">...</span>
    // 中略 ... 這邊放了 2000 個 span
</div>

那麼,我們可以把這個事件往外放:

<div id="container" @click.prevent="doSomething($event)">
    <span>...</span>
    // 中略 ... 這邊放了 2000 個 span
</div>

這麼做可以省下 綁定了 2000 次 這件事情,最終只會綁定一次事件而已。但是,這麼做你的事件對象就會改變,你必須要自己處理事件對象的相關判斷,換句話說,你原本的 $event.target 可能會是 <span> ,當你往上提的時候,他有可能會是 <div> 也有可能是 <span> ,當然也會有可能是 #text 節點。

所以,扣除掉 事件對象 這件事情比較麻煩以外,只綁定一次,跟綁定了 2000 次比起來,就能省下不少 JavaScript 在處理的時間。其實無論是何種框架,當你的虛擬節點( Visual DOM )需要處理這麼大量的資訊時,所耗費的時間是相當可觀的。


原生的好

雖然說原生的 JavaScript 用於處理 DOM 上面相對吃力,不過處理的好的話,基本上並不會影響到你所使用的框架。畢竟,你的框架不也都來自于源生的 JavaScript (誰沒有父母啊)。

但是,你也能把原生的寫得很爛就是了(這一點就不討論了)。

面對這種大量畫面產生的需求,另外一個面向是使用 字串 來處理。也就是說,你可以把 2000 個 <span> 當作字串,最後用 innerHTML 把他放到 DOM 結構樹裡面。也就是說,你可以不用每次都 document.createElement('span') 這樣做事。缺點就是那個字串可能會比你當初拿到的 JSON 還要更肥大就是。

但,純粹的組合字串一直都是相當快速的事情。

至於你說 Canvas ,因為我不太熟悉,所以跳過(欸)。


小結

基本上就是跳脫思考慣性,如果綁一次就可以解決,那麼為何一定要綁 2000 次呢?由於資料的關係所以我也不方便舉太多例子(笑)。

ITHome 鐵人賽同步刊登 大型資料載入實例與狀況 Event Binding Day 25