# 虚拟列表
虚拟就是仅渲染当前视窗内的内容,而对于超出的部分则进行移除
<!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" />
<script src="https://unpkg.com/vue@3.2.27/dist/vue.global.js"></script>
<title>VirtualScroll</title>
</head>
<body>
<div id="app">
<div
ref="demo"
class="scroll-box demo"
:style="`height: ${showNumber * itemHeight}px;`"
>
<div
class="scroll-blank"
:style="`height: ${data.length * itemHeight}px;`"
></div>
<div class="scroll-data" :style="`top: ${positionTop}px;`">
<div
v-for="(item, index) in activeList"
:key="item"
class="scroll-item"
>
{{ item }}
</div>
</div>
</div>
</div>
<script>
const { computed, onMounted, onUnmounted, ref } = Vue
const createData = (length) => {
return Object.keys(new Array(length).fill(''))
}
const App = {
setup() {
const demo = ref(null) // 外框盒子
const showNumber = 20 // 当前视窗展示条数
const itemHeight = 20 // 每一条内容的高度
const data = createData(1000) // 实际数据
let startNum = ref(0) // 当前视窗范围内第一个元素下标
let positionTop = ref(0) // 当前视窗范围内第一个元素偏移量
// 计算当前视窗内实际要渲染的内容
const activeList = computed(() => {
const start = startNum.value
return data.slice(start, start + showNumber)
})
// 滚动的时候计算当前视窗范围内第一个元素下标
const scrollEvent = (event) => {
const { scrollTop } = event.target
startNum.value = parseInt(scrollTop / itemHeight)
positionTop.value = scrollTop
}
onMounted(() => {
demo.value.addEventListener('scroll', scrollEvent)
})
onUnmounted(() => {
if (!demo.value) return
demo.value.removeEventListener('scroll', scrollEvent)
demo.value = null
})
return {
showNumber,
itemHeight,
demo,
positionTop,
data,
activeList,
}
},
}
const app = Vue.createApp(App)
app.mount('#app')
</script>
<style>
.scroll-box {
position: relative;
overflow: auto;
width: 400px;
border: 1px solid rgb(0, 0, 0);
}
.scroll-data {
position: absolute;
width: 100%;
}
.scroll-item {
height: 20px;
}
.scroll-item:hover {
background: rgb(104, 111, 211);
color: #fff;
}
</style>
</body>
</html>