Lesson 23 / 25

Performance Techniques

Render less, load less.

Tools for faster apps

Measure first with Vue Devtools and browser performance tools, then apply targeted fixes. Lazy loading: lazy routes and defineAsyncComponent(() => import(...)) split rarely used components into separate chunks. KeepAlive: wrapping dynamic components or a RouterView slot in <KeepAlive> caches instances instead of destroying them, preserving state; include/max control what is cached, and onActivated/onDeactivated fire on toggle. shallowRef tracks only .value replacement, avoiding deep conversion of large immutable data. v-memo (Vue 3.2+) skips re-rendering a subtree unless the listed values change, and v-once renders static content once. For very long lists, use virtual scrolling.

Async components, KeepAlive, shallowRef and v-memo

Several techniques in one view.

<script setup>
import { ref, shallowRef, defineAsyncComponent } from 'vue'

// loaded only when first rendered
const HeavyChart = defineAsyncComponent(() => import('./HeavyChart.vue'))
import DataTable from './DataTable.vue'

// large data replaced as a whole, not mutated deeply
const rows = shallowRef([])
async function load() {
  rows.value = await (await fetch('/api/rows')).json()  // replacing .value triggers updates
}

const selectedId = ref(null)
const tab = ref('table')
</script>

<template>
  <KeepAlive :max="5">
    <component :is="tab === 'chart' ? HeavyChart : DataTable" :rows="rows" />
  </KeepAlive>

  <div v-for="row in rows" :key="row.id"
       v-memo="[row.id === selectedId]"
       @click="selectedId = row.id">
    {{ row.name }}
  </div>
</template>

Do not optimise blindly

v-memo and shallowRef add complexity and can cause stale UI if dependencies are missed. Use them where profiling shows a real bottleneck.

Quick check: What is the main effect of wrapping a component in <KeepAlive>?

  • Its instance is cached when hidden, so state is preserved when shown again
  • It is rendered on the server
  • Its props become read-only
  • It is loaded in a separate chunk
Answer

Its instance is cached when hidden, so state is preserved when shown again — Cached instances fire onActivated and onDeactivated.