虚拟列表

4/5/2020 Vue技巧

# 虚拟列表

虚拟就是仅渲染当前视窗内的内容,而对于超出的部分则进行移除

<!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>
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

基于Vue3的极简虚拟滚动实现方案 (opens new window)

Last Updated: 4/17/2023, 11:22:39 PM