Blog>
Snippets

Vue 3 Performance Optimization with Virtual Scrolling

Implement virtual scrolling in a Vue 3 app to display large lists of items efficiently on mobile devices, reducing the DOM footprint and improving FPS.
import { createApp } from 'vue';
import App from './App.vue';

const app = createApp(App);
// Register your global components here, if any.

app.mount('#app');
This is the entry point of a Vue 3 application where the App component, which will contain the virtual scrolling logic, is initialized and mounted to the DOM.
<template>
  <div class="virtual-scroll" ref="scrollContainer">
    <div v-for="item in visibleItems" :key="item.id" class="list-item">
      <!-- Your item component/content here -->
      {{ item.content }}
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, computed, watch } from 'vue';
import { useVirtualScrolling } from '@/composables/useVirtualScrolling';

const props = defineProps({
  items: Array,
  itemHeight: Number
});

const scrollContainer = ref(null);
const { start, end, visibleItems } = useVirtualScrolling(scrollContainer, props.items, props.itemHeight);

onMounted(() => {
  const resizeObserver = new ResizeObserver(() => {
    // Recalculate visible items on container resize
    start.value = calculateStartIndex(scrollContainer.value.scrollTop, props.itemHeight);
    end.value = calculateEndIndex(start.value, scrollContainer.value.clientHeight, props.itemHeight, props.items.length);
  });
  resizeObserver.observe(scrollContainer.value);
});
</script>
Vue 3 component template and script setup for virtual scrolling. The 'useVirtualScrolling' composable is a custom hook that handles the calculations for visible items based on scroll position. 'visibleItems' is a computed property that depends on the 'start' and 'end' reactive references, which represent the range of items to display.
import { ref, computed, watch } from 'vue';

export function useVirtualScrolling(containerRef, items, itemHeight) {
  const start = ref(0);
  const end = ref(0);

  const visibleItems = computed(() => items.slice(start.value, end.value));

  const handleScroll = () => {
    const scrollTop = containerRef.value.scrollTop;
    start.value = calculateStartIndex(scrollTop, itemHeight);
    end.value = calculateEndIndex(start.value, containerRef.value.clientHeight, itemHeight, items.length);
  };

  watch(containerRef, (newValue, oldValue) => {
    if (newValue) {
      newValue.addEventListener('scroll', handleScroll);
    }
    if (oldValue) {
      oldValue.removeEventListener('scroll', handleScroll);
    }
  });

  return { start, end, visibleItems };
}

function calculateStartIndex(scrollTop, itemHeight) {
  return Math.floor(scrollTop / itemHeight);
}

function calculateEndIndex(startIndex, clientHeight, itemHeight, totalItems) {
  const endIndex = startIndex + Math.ceil(clientHeight / itemHeight);
  return Math.min(endIndex, totalItems);
}
Definition of the 'useVirtualScrolling' composable function that is responsible for managing virtual scrolling. It creates reactive references for start and end indices, computed property for visible items, and sets up a scroll event listener to recompute visible items as the user scrolls through the container.