若是要在前端呈現大量的數據,通常的策略就是分頁。前端要呈現百萬數據,這個需求是不多見的,可是展現千條稍微複雜點的數據,這種需求仍是比較常見,只要內存夠,javascript 確定是吃得消的,計算幾千上萬條數據,js 效率根本不在話下,可是 DOM 的渲染瀏覽器扛不住,CPU 稍微搓點的電腦必然會卡爆。javascript
本文的策略是,顯示三屏數據,其餘的移除 DOM。css
下面是我簡單勾畫的一個草圖,咱們把一串數據放到一個容器當中,這串數據的高度(Data List)確定是比 Container 的高度要高不少的,若是咱們一次性把數據都顯示出來,瀏覽器須要花費大量的時間來計算每一個 data 的位置,而且依次渲染出來,整個過程當中 JS 並無花費太多的時間,開銷主要是 DOM 渲染。html
/==============> Data List | .... | / +--------------+/ +=======|=====data=====|========+ | +--------------+ | | | data | | | +--------------+ |\ | | data | | \ | +--------------+ | \======> Container +=======|=====data=====|========+ +--------------+ | .... | Created By Barret Lee
爲了解決這個問題,咱們讓數據是顯示一部分,這一部分是 Container 可視區域的內容,以及上下各一屏(一屏指的是 Container 高度所能容納的區域大小)的緩存內容。若是 Container 比較高,也但是隻緩存半屏,緩存的緣由是,在咱們滾動滾動條的時候,js 須要時間來拼湊字符串(或者建立 Node ),這個時候瀏覽器還來不及渲染,因此會出現臨時的空白,這種體驗是至關很差的。前端
demo 在 IE 七、8 有 bug,請讀者本身修復吧~java
代碼:git
<title>百萬數據前端快速流暢顯示</title> <style type="text/css"> #box {position: relative; height: 300px; width: 200px; border:1px solid #CCC; overflow: auto} #box div { position: absolute; height: 20px; width: 100%; left: 0; overflow: hidden; font: 16px/20px Courier;} </style> <div id="box"></div> <script type="text/javascript"> var total = 1e5 , len = total , height = 300 , delta = 20 , num = height / delta , data = []; for(var i = 0; i < total; i++){ data.push({content: "item-" + i}); } var box = document.getElementById("box"); box.onscroll = function(){ var sTop = box.scrollTop||0 , first = parseInt(sTop / delta, 10) , start = Math.max(first - num, 0) , end = Math.min(first + num, len - 1) , i = 0; for(var s = start; s <= end; s++){ var child = box.children[s]; if(!box.contains(child) && s != len - 1){ insert(s); } } while(child = box.children[i++]){ var index = child.getAttribute("data-index"); if((index > end || index < start) && index != len - 1){ box.removeChild(child); } } }; function insert(i){ var div = document.createElement("div"); div.setAttribute("data-index", i); div.style.top = delta * i + "px"; div.appendChild(document.createTextNode(data[i].content)); box.appendChild(div); } box.onscroll(); insert(len - 1); </script>
能夠戳這個 demo,或者看這裏 https://gist.github.com/barretlee/9744160github
| | +=======|==============|========+—— | ↓——+--------------+ | ↑ | delta | | | | ↑——+--------------+ | height | | | | | +--------------+ | ↓ +=======|==============|========+—— | |
Container 能夠容納的 Data 數目爲 num = height / delta
,Container 頂部第一個節點的索引值爲算法
var first = parseInt(Container.scrollTop / delta);
因爲咱們上下都有留出一屏,因此segmentfault
var start = Math.max(first - num, 0); var end = Math.min(first + num, len - 1);
經過上面的計算,從 start 到 end 將節點一次插入到 Container 中,而且將最後一個節點插入到 DOM 中。瀏覽器
// 插入最後一個節點 insert(len - 1); // 插入從 start 到 end 之間的節點 for(var s = start; s <= end; s++){ var child = Container.children[s]; // 若是 Container 中已經有該節點,或者該節點爲最後一個節點則跳過 if(!Container.contains(child) && s != len - 1){ insert(s); } }
這裏解釋下爲何要插入最後一個節點,插入節點的方式是:
function insert(i){ var div = document.createElement("div"); div.setAttribute("data-index", i); div.style.top = delta * i + "px"; div.appendChild(document.createTextNode(data[i].content)); Container.appendChild(div); }
能夠看到咱們給插入的節點都加了一個 top 屬性,最後一個節點的 top 是最大的,只有把這個節點插入到 DOM 中,才能讓滾動條拉長,讓人感受放了不少的數據。
爲了減小瀏覽器的重排(reflow),咱們能夠隱藏三屏以外的數據。我這裏爲了方便,直接給刪除掉了,後續須要再從新插入。
while(child = Container.children[i++]){ var index = child.getAttribute("data-index"); // 這裏記得不要把最後一個節點給刪除掉了 if((index > end || index < start) && index != len - 1){ Container.removeChild(child); } }
當 DOM 加載完畢以後,觸發一次 Container.onscroll()
,而後整個程序就 OK 了。
本文主要是敘述大數據加載的一點基本原理,程序可能有 bug,也有不少地方能夠優化,瞭解下算法就好了
本文轉自:http://blog.segmentfault.com/barretlee/1190000000445290