virtual list 源码学习
在近期项目中使用了vue-virtual-scroll-list(虚拟滚动列表),觉得有很多值得学习的地方,以个人理解总结了一下虚拟滚动的实现原理
流程梳理
组件参数
- buffer: 10 缓存数量
- estimateSize: 50 估计的高度
- keeps: 30 显示数量,默认
- slotFooterSize: 0 尾部插槽
- slotHeaderSize: 0 头部插槽
- size 存储每个item高度 动态高度时需要用到
- calcType 固定高度还是非固定
- offset 保存偏移值
- diretion 方向
- range 需要返回给组件的值包括start,end,padFront,padBehind
固定高度和非固定主要区别
获取start方法不同(虚拟滚动的关键,通过滚动每次计算从哪里开始截取数据)
固定高度
// if is fixed type, that can be easily if (this.isFixedType()) { return Math.floor(offset / this.fixedSizeValue) }通过偏移和固定高度比值获得start
非固定高度
getScrollOvers () { let low = 0 let middle = 0 let middleOffset = 0 let high = this.param.uniqueIds.length while (low <= high) { // this.__bsearchCalls++ middle = low + Math.floor((high - low) / 2) middleOffset = this.getIndexOffset(middle) if (middleOffset === offset) { return middle } else if (middleOffset < offset) { low = middle + 1 } else if (middleOffset > offset) { high = middle - 1 } } return low > 0 ? --low : 0 }通过二分查找,高效找到符合middleOffset === offset条件的值,就是我们要找的start.
this.param.uniqueIds 总数据
获取pad方法不同
固定高度
f (this.isFixedType()) { return this.fixedSizeValue * this.range.start }固定高度*开始项 就获得了pad值
非固定高度
getIndexOffset (givenIndex) { if (!givenIndex) { return 0 } let offset = 0 let indexSize = 0 for (let index = 0; index < givenIndex; index++) { // this.__getIndexOffsetCalls++ indexSize = this.sizes.get(this.param.uniqueIds[index]) offset = offset + (typeof indexSize === 'number' ? indexSize : this.getEstimateSize()) } // remember last calculate index this.lastCalcIndex = Math.max(this.lastCalcIndex, givenIndex - 1) this.lastCalcIndex = Math.min(this.lastCalcIndex, this.getLastIndex()) return offset }通过获得的序号,循环累加每个item的偏移值,获得总的偏移值
其他学习的地方
item如何告知组件高度
mounted () { if (typeof ResizeObserver !== 'undefined') { this.resizeObserver = new ResizeObserver(() => { this.dispatchSizeChange() }) this.resizeObserver.observe(this.$el) } },通过ResizeObserver api来派发,性能较高,兼容性较差
render组件
render (h) { const { header, footer } = this.$slots const { padFront, padBehind } = this.range const { isHorizontal, pageMode, rootTag, wrapTag, wrapClass, wrapStyle, headerTag, headerClass, headerStyle, footerTag, footerClass, footerStyle } = this const paddingStyle = { padding: isHorizontal ? `0px ${padBehind}px 0px ${padFront}px` : `${padFront}px 0px ${padBehind}px` } const wrapperStyle = wrapStyle ? Object.assign({}, wrapStyle, paddingStyle) : paddingStyle return h(rootTag, { ref: 'root', on: { '&scroll': !pageMode && this.onScroll } }, [ // header slot header ? h(Slot, { class: headerClass, style: headerStyle, props: { tag: headerTag, event: EVENT_TYPE.SLOT, uniqueKey: SLOT_TYPE.HEADER } }, header) : null, // main list h(wrapTag, { class: wrapClass, attrs: { role: 'group' }, style: wrapperStyle }, this.getRenderSlots(h)), // footer slot footer ? h(Slot, { class: footerClass, style: footerStyle, props: { tag: footerTag, event: EVENT_TYPE.SLOT, uniqueKey: SLOT_TYPE.FOOTER } }, footer) : null, // an empty element use to scroll to bottom h('div', { ref: 'shepherd', style: { width: isHorizontal ? '0px' : '100%', height: isHorizontal ? '100%' : '0px' } }) ]) } })能看到组件分成三块渲染
设计Pad的目的
个人理解,通过动态计算上下的padding来撑开父级,才能scroll
二分查找可以提高多少性能
大概可以提升200%-500%,数据越大性能提升越明显,时间复杂度为O(lgN)
测试代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> </body> <script> let offset = 47586 let sizes=[ { "key": "1$d55ccb", "value": 132 }, { "key": "2$fbfd64", "value": 132 }, { "key": "3$43646e", "value": 104 }, { "key": "4$0e31a1", "value": 104 }, { "key": "5$61a16", "value": 132 }, { "key": "6$84fbe3", "value": 190 }, { "key": "7$dc29f6", "value": 132 }, { "key": "8$2cd2dc", "value": 132 }, { "key": "9$2544f6", "value": 132 }, { "key": "10$aa2d4a", "value": 132 }, { "key": "11$a24972", "value": 132 }, { "key": "12$ab8631", "value": 161 }, { "key": "13$93b821", "value": 132 }, { "key": "14$922d2", "value": 132 }, { "key": "15$04cc4", "value": 104 }, { "key": "16$537b28", "value": 132 }, { "key": "17$4dc083", "value": 190 }, { "key": "18$99772d", "value": 161 }, { "key": "19$23c0fd", "value": 161 }, { "key": "20$bc2708", "value": 104 }, { "key": "21$0113fc", "value": 161 }, { "key": "22$6e51c1", "value": 132 }, { "key": "23$a87899", "value": 132 }, { "key": "24$dd11b", "value": 132 }, { "key": "25$763aa5", "value": 161 }, { "key": "26$a9f5bf", "value": 132 }, { "key": "27$74bf4a", "value": 132 }, { "key": "28$08bb46", "value": 132 }, { "key": "29$aa859b", "value": 132 }, { "key": "30$340dde", "value": 104 }, { "key": "54$f0e3d2", "value": 104 }, { "key": "53$425e8d", "value": 132 }, { "key": "52$6d895b", "value": 104 }, { "key": "51$266f56", "value": 132 }, { "key": "50$807132", "value": 161 }, { "key": "49$638dcc", "value": 132 }, { "key": "48$024504", "value": 132 }, { "key": "47$5c4267", "value": 190 }, { "key": "46$8ed9c3", "value": 161 }, { "key": "45$7cbe93", "value": 132 }, { "key": "44$c2318c", "value": 132 }, { "key": "43$6cf5f6", "value": 132 }, { "key": "42$69bdc4", "value": 161 }, { "key": "41$ae6a9b", "value": 132 }, { "key": "40$327aa", "value": 104 }, { "key": "39$7ae6a3", "value": 161 }, { "key": "38$8bba78", "value": 161 }, { "key": "37$64a1fb", "value": 132 }, { "key": "36$d0784d", "value": 132 }, { "key": "35$f372be", "value": 161 }, { "key": "34$9a4e0f", "value": 161 }, { "key": "33$3bb063", "value": 161 }, { "key": "32$e335f3", "value": 161 }, { "key": "31$4e2e3a", "value": 161 }, { "key": "91$a78c5b", "value": 104 }, { "key": "90$f465e", "value": 132 }, { "key": "89$72d965", "value": 132 }, { "key": "88$3a70b4", "value": 132 }, { "key": "87$d2877c", "value": 161 }, { "key": "86$020977", "value": 161 }, { "key": "85$3ed80a", "value": 104 }, { "key": "84$43a912", "value": 161 }, { "key": "83$dcbdcc", "value": 104 }, { "key": "82$d2377b", "value": 104 }, { "key": "81$57d10a", "value": 132 }, { "key": "80$a71af", "value": 132 }, { "key": "79$74fb4f", "value": 104 }, { "key": "78$50b98a", "value": 161 }, { "key": "77$1bd70b", "value": 132 }, { "key": "76$df3171", "value": 132 }, { "key": "75$04ad99", "value": 132 }, { "key": "74$b6c2db", "value": 132 }, { "key": "73$83851f", "value": 132 }, { "key": "72$103362", "value": 104 }, { "key": "71$6a4a26", "value": 132 }, { "key": "70$5ec82", "value": 132 }, { "key": "69$1663dc", "value": 104 }, { "key": "68$40a2d6", "value": 161 }, { "key": "67$babac4", "value": 132 }, { "key": "66$19533d", "value": 104 }, { "key": "65$9a7755", "value": 161 }, { "key": "64$3aa185", "value": 161 }, { "key": "63$c52989", "value": 132 }, { "key": "62$8f9e9f", "value": 104 }, { "key": "406$2e318a", "value": 161 }, { "key": "405$70a60e", "value": 132 }, { "key": "404$e13aab", "value": 132 }, { "key": "403$ac266f", "value": 132 }, { "key": "402$eab295", "value": 190 }, { "key": "401$72a4d7", "value": 104 }, { "key": "400$1349ca", "value": 132 }, { "key": "399$cdecf8", "value": 104 }, { "key": "398$f0c068", "value": 161 }, { "key": "397$52696f", "value": 161 }, { "key": "396$edbe5", "value": 104 }, { "key": "395$8fdb4f", "value": 132 }, { "key": "394$dbe7a9", "value": 104 }, { "key": "393$90312d", "value": 132 }, { "key": "392$6384bc", "value": 132 }, { "key": "391$1dea1d", "value": 161 }, { "key": "390$5542e7", "value": 161 }, { "key": "389$83601f", "value": 132 }, { "key": "388$027291", "value": 104 }, { "key": "387$cb3cd5", "value": 132 }, { "key": "386$e516a9", "value": 132 }, { "key": "385$098f3", "value": 132 }, { "key": "384$b36a33", "value": 132 }, { "key": "383$e2ac1d", "value": 161 }, { "key": "382$bee39", "value": 132 }, { "key": "381$b7dbc7", "value": 104 }, { "key": "380$27574a", "value": 132 }, { "key": "379$47d162", "value": 190 }, { "key": "378$d3e4f3", "value": 161 }, { "key": "377$7fd8b1", "value": 132 }, { "key": "698$a9eb3c", "value": 161 }, { "key": "697$80d263", "value": 132 }, { "key": "696$2e829a", "value": 132 }, { "key": "695$c42128", "value": 132 }, { "key": "694$14bc06", "value": 161 }, { "key": "693$197161", "value": 104 }, { "key": "692$ed0e83", "value": 132 }, { "key": "691$de1cf1", "value": 161 }, { "key": "690$595a57", "value": 161 }, { "key": "689$dd60e3", "value": 161 }, { "key": "688$135422", "value": 161 }, { "key": "687$bc938d", "value": 132 }, { "key": "686$3ad86f", "value": 104 }, { "key": "685$83f704", "value": 104 }, { "key": "684$372ce5", "value": 132 }, { "key": "683$ac126f", "value": 104 }, { "key": "682$a81dc9", "value": 104 }, { "key": "681$0d3674", "value": 132 }, { "key": "680$58c1d9", "value": 132 }, { "key": "679$65d7b2", "value": 132 }, { "key": "678$f9da54", "value": 104 }, { "key": "677$1b7166", "value": 132 }, { "key": "676$b4676c", "value": 161 }, { "key": "675$f1460e", "value": 132 }, { "key": "674$86b393", "value": 190 }, { "key": "673$860924", "value": 132 }, { "key": "672$afadaa", "value": 161 }, { "key": "671$044fe8", "value": 132 }, { "key": "670$71ebf5", "value": 104 }, { "key": "669$175f18", "value": 190 }, { "key": "948$70acb6", "value": 104 }, { "key": "947$01a2ff", "value": 132 }, { "key": "946$e72281", "value": 132 }, { "key": "945$1ce527", "value": 132 }, { "key": "944$11b33a", "value": 104 }, { "key": "943$1056a", "value": 132 }, { "key": "942$fa627", "value": 104 }, { "key": "941$1d0f1b", "value": 104 }, { "key": "940$826b7a", "value": 161 }, { "key": "939$394fdd", "value": 161 }, { "key": "938$a26e88", "value": 161 }, { "key": "937$922b79", "value": 190 }, { "key": "936$1cc591", "value": 132 }, { "key": "935$8db7bb", "value": 104 }, { "key": "934$547c03", "value": 190 }, { "key": "933$0a0f22", "value": 161 }, { "key": "932$83f291", "value": 132 }, { "key": "931$a31969", "value": 161 }, { "key": "930$1b4e86", "value": 161 }, { "key": "929$f96ab8", "value": 161 }, { "key": "928$e26e55", "value": 132 }, { "key": "927$f2c09f", "value": 132 }, { "key": "926$99d04d", "value": 104 }, { "key": "925$9e4e32", "value": 104 }, { "key": "924$5b2acf", "value": 161 }, { "key": "923$9a7242", "value": 104 }, { "key": "922$73ecf3", "value": 161 }, { "key": "921$1f76c4", "value": 161 }, { "key": "920$f885a6", "value": 161 }, { "key": "919$a8839b", "value": 104 }, { "key": "1136$8fff4e", "value": 132 }, { "key": "1135$3979e7", "value": 132 }, { "key": "1134$25c023", "value": 161 }, { "key": "1133$e69df3", "value": 104 }, { "key": "1132$e520c4", "value": 132 }, { "key": "1131$22d7c7", "value": 132 }, { "key": "1130$ce59e7", "value": 132 }, { "key": "1129$e77032", "value": 161 }, { "key": "1128$9b3c85", "value": 132 }, { "key": "1127$481f41", "value": 104 }, { "key": "1126$404032", "value": 132 }, { "key": "1125$8ef46", "value": 161 }, { "key": "1124$68459f", "value": 132 }, { "key": "1123$48faad", "value": 161 }, { "key": "1122$301504", "value": 104 }, { "key": "1121$aeaebb", "value": 132 }, { "key": "1120$abcc98", "value": 132 }, { "key": "1119$474364", "value": 132 }, { "key": "1118$a6f666", "value": 132 }, { "key": "1117$c07e53", "value": 132 }, { "key": "1116$4560b2", "value": 161 }, { "key": "1115$d66129", "value": 132 }, { "key": "1114$dda82e", "value": 104 }, { "key": "1113$7685fa", "value": 132 }, { "key": "1112$3071c9", "value": 132 }, { "key": "1111$ca50ea", "value": 190 }, { "key": "1110$8b34c3", "value": 132 }, { "key": "1109$16ef41", "value": 161 }, { "key": "1108$df9efd", "value": 132 }, { "key": "1107$46834f", "value": 132 }, { "key": "1366$360ba7", "value": 132 }, { "key": "1365$efac74", "value": 132 }, { "key": "1364$3113b9", "value": 104 }, { "key": "1363$177a49", "value": 161 }, { "key": "1362$c697bc", "value": 190 }, { "key": "1361$ab88f9", "value": 104 }, { "key": "1360$15c4f1", "value": 104 }, { "key": "1359$4c5547", "value": 161 }, { "key": "1358$1b2cef", "value": 104 }, { "key": "1357$46263e", "value": 132 }, { "key": "1356$9366b7", "value": 104 }, { "key": "1355$c6274d", "value": 132 }, { "key": "1354$ed7066", "value": 132 }, { "key": "1353$1fe228", "value": 104 }, { "key": "1352$5e3b24", "value": 104 }, { "key": "1351$938226", "value": 132 }, { "key": "1350$04e5e1", "value": 132 }, { "key": "1349$cbc5b1", "value": 132 }, { "key": "1348$131023", "value": 132 }, { "key": "1347$57d994", "value": 161 }, { "key": "1346$4290ad", "value": 104 }, { "key": "1345$eb0f5", "value": 132 }, { "key": "1344$0c3255", "value": 161 }, { "key": "1343$defbd7", "value": 104 }, { "key": "1342$a0082b", "value": 132 }, { "key": "1341$e066ec", "value": 161 }, { "key": "1340$1b647f", "value": 132 }, { "key": "1339$f35dcc", "value": 132 }, { "key": "1338$03c941", "value": 161 }, { "key": "1337$4a250b", "value": 104 }, { "key": "1576$3b0ad2", "value": 132 }, { "key": "1575$9c64e4", "value": 104 }, { "key": "1574$289741", "value": 104 }, { "key": "1573$e456b6", "value": 161 }, { "key": "1572$21d422", "value": 132 }, { "key": "1571$5e13d3", "value": 132 }, { "key": "1570$40c99", "value": 132 }, { "key": "1569$e88f73", "value": 161 }, { "key": "1568$22f44", "value": 104 }, { "key": "1567$6c8991", "value": 104 }, { "key": "1566$5c7c47", "value": 104 }, { "key": "1565$5d4dfb", "value": 104 }, { "key": "1564$eed267", "value": 104 }, { "key": "1563$558562", "value": 132 }, { "key": "1562$4871e3", "value": 104 }, { "key": "1561$6cacf3", "value": 132 }, { "key": "1560$bf4de9", "value": 104 }, { "key": "1559$947483", "value": 132 }, { "key": "1558$ad685b", "value": 161 }, { "key": "1557$341b5a", "value": 132 }, { "key": "1556$27d4fa", "value": 190 }, { "key": "1555$fb1154", "value": 132 }, { "key": "1554$67e73c", "value": 104 }, { "key": "1553$b48a67", "value": 161 }, { "key": "1552$c35efb", "value": 132 }, { "key": "1551$6b524b", "value": 132 }, { "key": "1550$ddfa6b", "value": 104 }, { "key": "1549$7932cd", "value": 190 }, { "key": "1548$eb61ec", "value": 132 }, { "key": "1547$112b0f", "value": 161 }, { "key": "1806$bf2695", "value": 161 }, { "key": "1805$61b9df", "value": 104 }, { "key": "1804$e40a69", "value": 161 }, { "key": "1803$f84e86", "value": 161 }, { "key": "1802$94d275", "value": 132 }, { "key": "1801$43796e", "value": 132 }, { "key": "1800$346c57", "value": 104 }, { "key": "1799$173452", "value": 132 }, { "key": "1798$56abe5", "value": 104 }, { "key": "1797$50eb7d", "value": 161 }, { "key": "1796$b9bac2", "value": 161 }, { "key": "1795$0c5c15", "value": 132 }, { "key": "1794$d06d1a", "value": 132 }, { "key": "1793$8e77f", "value": 161 }, { "key": "1792$fbeb62", "value": 161 }, { "key": "1791$dc459d", "value": 132 }, { "key": "1790$9369c7", "value": 132 }, { "key": "1789$2139ae", "value": 161 }, { "key": "1788$a9b702", "value": 132 }, { "key": "1787$59a84a", "value": 132 }, { "key": "1786$f4fbf", "value": 104 }, { "key": "1785$f17d85", "value": 161 }, { "key": "1784$628f04", "value": 161 }, { "key": "1783$3808f5", "value": 104 }, { "key": "1782$16a23b", "value": 132 }, { "key": "1781$39f145", "value": 104 }, { "key": "1780$835ecf", "value": 132 }, { "key": "1779$db6ce7", "value": 104 }, { "key": "1778$03932e", "value": 161 }, { "key": "1777$77219c", "value": 190 }, { "key": "1952$33b19d", "value": 132 }, { "key": "1951$c57f57", "value": 132 }, { "key": "1950$9d4a05", "value": 161 }, { "key": "1949$14c9e7", "value": 161 }, { "key": "1948$82e67a", "value": 132 }, { "key": "1947$f60f39", "value": 104 }, { "key": "1946$ac3a0d", "value": 104 }, { "key": "1945$fdf223", "value": 104 }, { "key": "1944$7ac05c", "value": 132 }, { "key": "1943$fb9296", "value": 104 }, { "key": "1942$1f0f35", "value": 132 }, { "key": "1941$b9bac2", "value": 132 }, { "key": "1940$3b3888", "value": 161 }, { "key": "1939$0abbfc", "value": 161 }, { "key": "1938$8a28fc", "value": 132 }, { "key": "1937$597188", "value": 132 }, { "key": "1936$7e6ff1", "value": 161 }, { "key": "1935$948342", "value": 104 }, { "key": "1934$4ab42a", "value": 132 }, { "key": "1933$105c03", "value": 132 }, { "key": "1932$57cd82", "value": 104 }, { "key": "1931$46ff55", "value": 104 }, { "key": "1930$cfc0c5", "value": 132 }, { "key": "1929$f2225f", "value": 104 }, { "key": "1928$d36698", "value": 132 }, { "key": "1927$af1da5", "value": 104 }, { "key": "1926$c9a89d", "value": 104 }, { "key": "1925$cd0a6f", "value": 132 }, { "key": "1924$935582", "value": 132 }, { "key": "1923$bfbfe3", "value": 161 }, { "key": "1995$aa44ae", "value": 104 }, { "key": "1994$4d09ae", "value": 161 }, { "key": "1993$c723a9", "value": 161 }, { "key": "1992$9d7f83", "value": 132 }, { "key": "1991$3cb338", "value": 132 }, { "key": "1990$3f8c6", "value": 161 }, { "key": "1989$52864f", "value": 132 }, { "key": "1988$bba521", "value": 132 }, { "key": "1987$715f2a", "value": 132 }, { "key": "1986$af3688", "value": 161 }, { "key": "1985$599a0e", "value": 161 }, { "key": "1984$b03a9e", "value": 104 }, { "key": "1983$18bdc9", "value": 161 }, { "key": "1982$cc1aa2", "value": 161 }, { "key": "1981$d099f8", "value": 161 }, { "key": "1980$a97947", "value": 104 }, { "key": "1979$67614", "value": 104 }, { "key": "1978$430def", "value": 104 }, { "key": "1977$7b4a9a", "value": 132 }, { "key": "1976$158e89", "value": 104 }, { "key": "1975$944011", "value": 104 }, { "key": "1974$c28922", "value": 132 }, { "key": "1973$c32547", "value": 161 }, { "key": "1972$d17ccc", "value": 104 }, { "key": "1971$e285cc", "value": 161 }, { "key": "1970$abd558", "value": 161 }, { "key": "1969$bf1171", "value": 104 }, { "key": "1968$4817fa", "value": 161 }, { "key": "1967$82b541", "value": 104 }, { "key": "1966$42b7f3", "value": 161 } ] function getScrollOvers(){ let low = 0 let middle = 0 let middleOffset = 0 let high = sizes.length while (low <= high) { middle = low + Math.floor((high - low) / 2) middleOffset = this.getIndexOffset(middle) if (middleOffset === offset) { return middle } else if (middleOffset < offset) { low = middle + 1 } else if (middleOffset > offset) { high = middle - 1 } } } function getIndexOffset (givenIndex) { let offset = 0 let indexSize = 0 for (let index = 0; index < givenIndex; index++) { indexSize = sizes[index].value offset = offset + indexSize } return offset } console.time('binarysearch') console.log(getScrollOvers()) console.timeEnd('binarysearch') function getScroll(){ for(let i=0;i<sizes.length;i++){ if(offset===getIndexOffset(i)){ return i } } } console.time('foreach') console.log(getScroll()) console.timeEnd('foreach') </script> </html>