963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
4.9 KiB
4.9 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| KeepAlive Memory Management - Prevent Memory Leaks | HIGH | Unbounded KeepAlive caching can cause severe memory issues, especially with large or numerous components | gotcha |
|
KeepAlive Memory Management - Prevent Memory Leaks
Impact: HIGH - KeepAlive caches component instances in memory. Without proper limits and cleanup, this can lead to memory exhaustion, especially in applications with many routes or large component trees.
Task Checklist
- Always use the
maxprop to limit cached instances - Clean up resources in
onDeactivatedhook - Monitor memory usage when using KeepAlive extensively
- Be cautious with KeepAlive on memory-heavy components
- Test on memory-constrained devices (mobile, low-spec laptops)
The Problem: Unbounded Cache Growth
<template>
<!-- DANGEROUS: No limit on cached components -->
<KeepAlive>
<component :is="currentView" />
</KeepAlive>
</template>
When users navigate through many different components, each instance stays in memory:
- Chrome memory grows continuously
- VueComponent count keeps increasing
- App becomes sluggish or crashes
Solution: Set Cache Limits
<template>
<!-- LRU cache: keeps max 10, evicts least recently used -->
<KeepAlive :max="10">
<component :is="currentView" />
</KeepAlive>
</template>
How max works:
- KeepAlive uses an LRU (Least Recently Used) cache algorithm
- When cache exceeds
max, the oldest unused component is destroyed - This caps memory usage to a predictable maximum
Choose max Based on Your Use Case
<template>
<!-- Tab-based UI: usually 3-5 tabs max -->
<KeepAlive :max="5">
<component :is="currentTab" />
</KeepAlive>
<!-- Route-based caching: limit to frequently visited pages -->
<router-view v-slot="{ Component }">
<KeepAlive :max="3" include="Dashboard,Settings,Profile">
<component :is="Component" />
</KeepAlive>
</router-view>
</template>
Clean Up Resources in Deactivated Hook
Even with max, cached components hold resources. Clean up when deactivated:
<script setup>
import { ref, onActivated, onDeactivated, onUnmounted } from 'vue';
const chartInstance = ref(null);
const websocket = ref(null);
const intervalId = ref(null);
onActivated(() => {
// Resume or recreate resources
websocket.value = new WebSocket('...');
intervalId.value = setInterval(fetchData, 5000);
});
onDeactivated(() => {
// IMPORTANT: Clean up to reduce memory footprint while cached
chartInstance.value?.destroy();
chartInstance.value = null;
websocket.value?.close();
websocket.value = null;
clearInterval(intervalId.value);
intervalId.value = null;
});
// Final cleanup when truly destroyed
onUnmounted(() => {
// Same cleanup for when component is evicted from cache
chartInstance.value?.destroy();
websocket.value?.close();
clearInterval(intervalId.value);
});
</script>
Third-Party Library Cleanup
Libraries that manipulate the DOM outside Vue need explicit cleanup:
<script setup>
import { ref, onMounted, onDeactivated, onUnmounted } from 'vue';
const mapContainer = ref(null);
let mapInstance = null;
onMounted(() => {
mapInstance = new MapLibrary(mapContainer.value);
});
onDeactivated(() => {
// Some map libraries hold significant memory
// Destroy and recreate on reactivation if needed
mapInstance?.remove();
mapInstance = null;
});
onUnmounted(() => {
mapInstance?.remove();
mapInstance = null;
});
</script>
Avoid KeepAlive for Memory-Heavy Components
Some components should NOT be cached:
<script setup>
const heavyComponents = ['VideoPlayer', 'LargeDataGrid', 'MapView'];
</script>
<template>
<!-- Exclude memory-heavy components from cache -->
<KeepAlive :exclude="heavyComponents" :max="5">
<component :is="currentView" />
</KeepAlive>
</template>
Monitor Memory in Development
<script setup>
import { onActivated, onDeactivated } from 'vue';
if (import.meta.env.DEV) {
onActivated(() => {
console.log('Activated - Memory:', performance.memory?.usedJSHeapSize);
});
onDeactivated(() => {
console.log('Deactivated - Memory:', performance.memory?.usedJSHeapSize);
});
}
</script>
Key Points
- Always set
max- Never use KeepAlive without a reasonable limit - Clean up in
onDeactivated- Don't wait for unmount to release resources - Exclude heavy components - Large data grids, media players, maps
- Test on target devices - Mobile users have less memory
- Monitor in development - Watch for growing memory usage