KeepAlive, Lazy Components, Virtual Lists
Performance Optimization
Most Vue apps are fast by default. When you do need to optimize, the high-leverage moves are lazy routes, shallowRef, v-memo, and virtual lists.
What you'll learn
- Lazy-load routes and components
- Use shallowRef for large data
- Apply v-memo to expensive list items
- Virtual-scroll long lists
Vue’s reactivity is fast, compile-time templates are well-optimized, and most apps don’t need tuning. When the profiler shows a hot spot, here are the moves that pay off most.
Lazy Components
Big components that aren’t on the initial paint path? defineAsyncComponent plus a dynamic import splits them into their own chunk.
import { defineAsyncComponent } from 'vue'
const Chart = defineAsyncComponent(() => import('./Chart.vue')) Combine with <Suspense> for loading UI.
shallowRef
ref deeply reacts to mutations — shallowRef only reacts when the ref is reassigned. For large arrays, geo data, or third-party objects you never mutate piece by piece, it’s significantly faster.
import { shallowRef } from 'vue'
const features = shallowRef<Feature[]>([])
// Reassign to trigger reactivity:
features.value = await fetchFeatures() v-once and v-memo
v-once marks a subtree as render-once.
<header v-once>{{ siteTitle }}</header> v-memo re-renders only when its dependency array changes — great for big lists where each row is expensive.
<li
v-for="item in items"
:key="item.id"
v-memo="[item.selected]"
>
…
</li> Virtual Lists
Rendering 10k DOM nodes is slow no matter what. Use a virtualizer that only renders what’s visible.
npm i @tanstack/vue-virtual Bundle Size
Heavy dependencies sneak in. Use rollup-plugin-visualizer to find them — most often it’s moment, full lodash, or chart libs imported as a whole.
Avoid Heavy Computeds
A computed recomputes when any of its dependencies change. Don’t put expensive work there if the deps change often — debounce, memoize, or move the work into an action.