vue3+ts 列表虚拟滚动功能

vue3+ts 列表虚拟滚动功能

vue3 + ts 项目中实现的列表虚拟滚动功能

效果图:
在这里插入图片描述

<template>
  <div class="virtual_scroller" :style="{height: height + 'px'}">
    <div class="content_box" :style="{'height': (allData.length*rowHeight)+'px'}">
      <div v-for="(item, index) in showList" class="row_item" :key="index" :style="{height: rowHeight + 'px'}">{{item.text}}</div>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted, nextTick } from 'vue'

interface IData{
  text: string
}

export default defineComponent({
  name: 'App',
  props: {
    scrollHeight: {
      type: Number,
      default: 500
    },
    itemHeight: {
      type: Number,
      default: 48
    }
  },
  setup(props) {
    let lastVisibleItemIndex = 0
    let allData: IData[] = []
    for(let i = 1; i < 1000; i++) {
      allData.push({text: `I am ${i}`})
    }
    const height = props.scrollHeight as number
    const rowHeight = props.itemHeight as number
    const pageSize = Math.ceil(height / rowHeight) + 5
    let showList = ref<IData[]>(allData.slice(0, pageSize))

    onMounted(() => {
      const scroller: HTMLElement = document.querySelector('.virtual_scroller') as HTMLElement
      const handleScroll = (e: Event) => {
        const {scrollTop} = e.target as HTMLElement
        const firstVisibleItemIndex = Math.floor(scrollTop / rowHeight)
        if(firstVisibleItemIndex > lastVisibleItemIndex) {
          showList.value = allData.slice(firstVisibleItemIndex, firstVisibleItemIndex + pageSize)
          nextTick(() => {
            let rows: NodeList = document.querySelectorAll('.row_item')
            rows.forEach((row: any) => row.style.transform = `translateY(${scrollTop}px)`)
          })
          lastVisibleItemIndex = firstVisibleItemIndex
        } else if(firstVisibleItemIndex < lastVisibleItemIndex) {
          // 往上滚
          if(lastVisibleItemIndex <= 3) {
            // 快滚动到顶点时,上面用于填充的三个不用再处理了,直接默认显示前三个就行了
            showList.value = allData.slice(0, pageSize)
            nextTick(() => {
              let rows: NodeList = document.querySelectorAll('.row_item')
              rows.forEach((row: any) => row.style.transform = `translateY(${0}px)`)
            })
          } else {
            // 往上滚的时候往上填充了三个的高度,否则滚动过快的时候会看到上面是空白的
            showList.value = allData.slice(firstVisibleItemIndex - 3, firstVisibleItemIndex + pageSize - 3)
            // 这里使用nextTick是因为showList.value的数据的变化,DOM元素个数也变化了,必须等渲染完再给他们位移,否则没有渲染出来的dom不会产生位移量
            nextTick(() => {
              let rows: NodeList = document.querySelectorAll('.row_item')
              rows.forEach((row: any) => row.style.transform = `translateY(${scrollTop - rowHeight * 3}px)`)
            })
          }
        }
        lastVisibleItemIndex = firstVisibleItemIndex
      }
      scroller.addEventListener('scroll', handleScroll)
    })
    return {
      height,
      rowHeight,
      showList,
      allData
    }
  }
})
</script>
<style>
.virtual_scroller{
  background-color: #f0f2f7;
  width: 800px;
  margin: auto;
  overflow: auto;
}
.row_item:nth-of-type(2n-1){
  background-color: pink;
  transition: all .1s;
}
.row_item:nth-of-type(2n){
  background-color: darkcyan;
  transition: all .1s;
}
</style>

当然你也可以根据这里实现的原理,使用js原生写出一个虚拟滚动的功能。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Vue3 + TypeScript 中使用 Virtualized Table(虚拟滚动表)是为了优化大型数据列表的性能,特别是当数据量非常大以至于一次性加载所有数据会消耗过多内存和影响性能时。Vuetify库提供了一个名为 `v-data-table` 的组件,它支持虚拟功能。 在 Vue3 with TypeScript 的项目中,你可以这样做: 1. 安装依赖:首先需要安装 Vuetify 和相关的虚拟滚动插件,比如 `vue-virtual-scroller` 或者 `@vuetify/vue3-composition-api` 中自带的虚拟滚动支持。 ```bash npm install vuetify @vuetify/vuetify @vueuse/core ``` 2. 引入并配置:在入口文件或主组件中引入并配置 Vuetify,并启用 TypeScript 支持。 ```typescript import { createApp } from 'vue'; import Vuetify from 'vuetify'; import App from './App.vue'; // Enable TypeScript for Vuetify import { VuetifyOptions } from '@vuetify/types'; const appOptions: VuetifyOptions = { components: { // Add any custom components here }, }; createApp(App) .use(Vuetify, appOptions) .mount('#app'); ``` 3. 使用 Virtualized Table:在需要展示大量数据的表格组件中,使用 `v-data-table` 并加上 `items-per-page`、`dense` 等属性,以及 `v-data-table-generator` 来生成虚拟滚动的数据源。 ```html <template> <v-data-table :items-per-page="50" // 设置每页显示项数 :headers="tableHeaders" :items="virtualItems" // 使用虚拟滚动数据源 dense row-key="id" class="mx-auto" > <!-- 表头 --> <v-data-header> <v-row> <v-col ...></v-col> <!-- 根据需要添加表头列 --> </v-row> </v-data-header> <!-- 每行数据 --> <v-data-footer slot="footer"></v-data-footer> </v-data-table> </template> <script lang="ts"> import { defineComponent, ref } from 'vue'; import { usePagination } from '@vueuse/core'; export default defineComponent({ setup() { const tableHeaders = ...; // 初始化表头数据 const { items, pageCount, nextPage, prevPage } = usePagination({ perPage: 50 }); const virtualItems = ref(items); // 使用虚拟滚动生成数据 // 更新虚拟滚动数据源 function updateVirtualItems() { if (pageCount > 1) { virtualItems.value = Array.from({ length: pageCount }, (_, index) => items.slice(index * 50, (index + 1) * 50)); } } // 首次加载或分页时更新数据源 updateVirtualItems(); return { tableHeaders, nextPage, prevPage, updateVirtualItems, }; }, }); </script> ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值