若是要在前端呈现大量的数据,通常的策略就是分页。前端要呈现百万数据,这个需求是不多见的,可是展现千条稍微复杂点的数据,这种需求仍是比较常见,只要内存够,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