阅读 42

基于Vue3的极简虚拟滚动实现方案,无额外插件引用,附有完整demo

不多bb,直接开整

定义变量

const demo = ref(null) // 外框盒子 const showNumber = 20 // 当前视窗展示条数 const itemHeight = 20 // 每一条内容的高度 const data = createData(1000) // 实际数据 let startNum = ref(0) // 当前视窗范围内第一个元素下标 let positionTop = ref(0) // 当前视窗范围内第一个元素偏移量 复制代码

滚动

虚拟滚动第一条件首先是要能滚动,那么外框盒子高度固定,设置overflow: auto;

<div     ref="demo"     class="scroll-box demo"     :style="`height: ${showNumber * itemHeight}px;`" ></div> 复制代码

然后外框盒子内部内置一个高度为理论上渲染全部内容的空div占位,用于展示滚动条

<div     :style="`height: ${data.length * itemHeight}px;`" ></div> 复制代码

虚拟

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

<template>     <div :style="`top: ${positionTop}px;`">         <div             v-for="(item, index) in activeList"             :key="item"         >             {{ item }}         </div>     </div> </template> <script> const activeList = computed(() => {     const start = startNum.value     return data.slice(start, start + showNumber) }) </script> 复制代码

什么时候对渲染的数据进行替换?

对外框盒子添加 scroll 的监听事件,在滚动的时候获取scrollTop的值并计算当前视窗范围内第一个元素的下标

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 }) 复制代码

最后

贴上完整demo

<!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>


作者:WaitForTheWind
链接:https://juejin.cn/post/7054088878877048869

文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐