面试题答案
一键面试思路
- 测量高度:为了实现虚拟化,需要提前知道每个列表项的高度。由于高度是动态变化的,不能在初始化时就确定,因此可以在每个列表项渲染完成后测量其高度。在Svelte中,可以利用
bind:this
来获取DOM元素,进而测量高度。 - 高度缓存:测量后的高度需要缓存起来,以便虚拟化算法使用。可以使用一个数组来存储每个列表项的高度,数组的索引与列表项的索引相对应。
- 虚拟化算法调整:传统的虚拟化算法假设列表项高度固定,在此场景下需要修改为根据缓存的高度来计算可见列表项的范围。计算可见区域内的起始索引和结束索引时,需要累计每个列表项的高度。
关键代码片段
- 测量并缓存高度:
<script>
let itemHeights = [];
function measureHeight(node) {
if (node) {
itemHeights.push(node.offsetHeight);
}
}
</script>
{#each data as item, index}
<div bind:this={measureHeight}>
{item.content}
</div>
{/each}
- 调整虚拟化算法:
<script>
let visibleStart = 0;
let visibleEnd = 0;
let scrollTop = 0;
function updateVisibleRange() {
let totalHeight = 0;
for (let i = 0; i < itemHeights.length; i++) {
totalHeight += itemHeights[i];
if (totalHeight >= scrollTop) {
visibleStart = i;
break;
}
}
totalHeight = 0;
for (let i = visibleStart; i < itemHeights.length; i++) {
totalHeight += itemHeights[i];
if (totalHeight >= scrollTop + viewportHeight) {
visibleEnd = i;
break;
}
}
}
</script>
这里 viewportHeight
是视口高度,可以通过 window.innerHeight
获取,scrollTop
是滚动条位置,可以通过监听 scroll
事件获取。最后在渲染时,只渲染 visibleStart
到 visibleEnd
之间的列表项。
{#each data.slice(visibleStart, visibleEnd + 1) as item, index}
<div>
{item.content}
</div>
{/each}