javascript简单实现一个虚拟列表,理解了原理其实挺简单的。
背景 在公司项目中,需要给商品配置大量的属性值,可能其中一个属性的值数量就有成百上千条。
一个商品会有很多属性,如果把这些属性和属性值同时都渲染出来,就会导致在一个页面上渲染很多的节点 ,导致浏览页面时卡顿
从而导致应用性能 和用户体验 都不好。
为了解决上述原因导致的应用性能和用户体验问题
可能比较常见的解决方法就是:分页 。
另一个就是近两年常听到的一个解决方法:虚拟列表 。
下面简单分析下虚拟列表的实现方式
分析 简单分析下,虚拟列表主要由以下几个部分构成:
实际列表: 指你所需要展示的全部数据渲染成的列表
可视区: 指数据需要展示时能看到的最大区域
已加载区
未加载区
如下图所示:
由图可以看出
我们实际所能看到的数据(即可视区展示的内容 )只占所有的数据的很小一部分
还有很大一部分是我们暂时不需要渲染出来的
如果我们一次性把其他不需要的数据也同时渲染到页面上,会造成DOM的浪费。
所以我们考虑:能不能只渲染可视区的DOM节点,其他的节点等需要用到的时候再渲染出来,使页面上始终只存在可视区的节点,这样就大大减少了DOM节点的数量。
实现 页面代码如下
#listView: 表示你需要展示列表内容的区域,即可视区 ,本文中为 body
高度
#zhanwei: 用来表示占位,是滚动条出现,它的高度就是所有数据渲染到页面时的高度,需要在js中计算
#listContent: 是实际列表内容的容器。
1 2 3 4 5 6 <div id ="listView" class ="list-view" > <div id ="zhanwei" class ="zhanwei" > </div > <ul id ="listContent" class ="list-content" > </ul > </div >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 body { height : 400px ; } .list-view { height : 400px ; position : relative; overflow-y : auto; } .zhanwei { position : absolute; top : 0 ; left : 0 ; right : 0 ; bottom : 0 ; z-index : -1 ; } .list-content li { height : 100px ; } .list-content li :nth-of-type (odd) { background : #00ccff ; } .list-content li :nth-of-type (even) { background : #ffcc00 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 function setLongData ( ) { let data = []; for (let i = 0 ; i < 1000000 ; i++) { data.push ({id : "item" + i, value : Math .random () * i}) } return data; } function selectDom (selector ) { return document .querySelector (selector) } function loadData (start, end ) { let sliceData = setLongData ().slice (start, end) let F = document .createDocumentFragment (); for (let i = 0 ; i < sliceData.length ; i++) { let li = document .createElement ("li" ); li.innerText = JSON .stringify (sliceData[i]) li.className = sliceData[i].id F.appendChild (li) } selectDom (".list-content" ).innerHTML = "" ; selectDom (".list-content" ).appendChild (F) } document .getElementById ("zhanwei" ).style .height = `${100 * setLongData().length} px` let count = Math .ceil (document .body .clientHeight / 100 ) let startIndex = 0 ; let endIndex = count; loadData (startIndex, endIndex)function scrollFunction ( ) { let scrollTop = document .getElementById ("listView" ).scrollTop ; startIndex = Math .floor (scrollTop / 100 ); endIndex = startIndex + count; loadData (startIndex, endIndex) document .getElementById ("listContent" ).style .transform = `translate3d(0, ${startIndex * 100 } px, 0)` } document .getElementById ("listView" ).addEventListener ("scroll" , scrollFunction)
最终结果如图,可以看出DOM节点的数量始终没有变化
最后 这只是一个简单实现,要在实际项目中应用还需要深入研究。
感谢您的阅读