Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Enforce Vue 3 best practices—Composition API, script setup, TypeScript, component boundaries, and reactivity patterns
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/updated-hook-performance.md
1---2title: Avoid Expensive Operations in Updated Hook3impact: MEDIUM4impactDescription: Heavy computations in updated hook cause performance bottlenecks and potential infinite loops5type: capability6tags: [vue3, vue2, lifecycle, updated, performance, optimization, reactivity]7---89# Avoid Expensive Operations in Updated Hook1011**Impact: MEDIUM** - The `updated` hook runs after every reactive state change that causes a re-render. Placing expensive operations, API calls, or state mutations here can cause severe performance degradation, infinite loops, and dropped frames below the optimal 60fps threshold.1213Use `updated`/`onUpdated` sparingly for post-DOM-update operations that cannot be handled by watchers or computed properties. For most reactive data handling, prefer watchers (`watch`/`watchEffect`) which provide more control over what triggers the callback.1415## Task List1617- Never perform API calls in updated hook18- Never mutate reactive state inside updated (causes infinite loops)19- Use conditional checks to verify updates are relevant before acting20- Prefer `watch` or `watchEffect` for reacting to specific data changes21- Use throttling/debouncing if updated operations are expensive22- Reserve updated for low-level DOM synchronization tasks2324**BAD:**25```javascript26// BAD: API call in updated - fires on every re-render27export default {28data() {29return { items: [], lastUpdate: null }30},31updated() {32// This runs after every single state change!33fetch('/api/sync', {34method: 'POST',35body: JSON.stringify(this.items)36})37}38}39```4041```javascript42// BAD: State mutation in updated - infinite loop43export default {44data() {45return { renderCount: 0 }46},47updated() {48// This causes another update, which triggers updated again!49this.renderCount++ // Infinite loop50}51}52```5354```javascript55// BAD: Heavy computation on every update56export default {57updated() {58// Expensive operation runs on every keystroke, every state change59this.processedData = this.heavyComputation(this.rawData)60this.analytics = this.calculateMetrics(this.allData)61}62}63```6465**GOOD:**66```javascript67import debounce from 'lodash-es/debounce'6869// GOOD: Use watcher for specific data changes70export default {71data() {72return { items: [] }73},74watch: {75// Only fires when items actually changes76items: {77handler(newItems) {78this.syncToServer(newItems)79},80deep: true81}82},83methods: {84syncToServer: debounce(function(items) {85fetch('/api/sync', {86method: 'POST',87body: JSON.stringify(items)88})89}, 500)90}91}92```9394```vue95<!-- GOOD: Composition API with targeted watchers -->96<script setup>97import { ref, watch, onUpdated } from 'vue'98import { useDebounceFn } from '@vueuse/core'99100const items = ref([])101const scrollContainer = ref(null)102103// Watch specific data - not all updates104watch(items, (newItems) => {105syncToServer(newItems)106}, { deep: true })107108const syncToServer = useDebounceFn((items) => {109fetch('/api/sync', { method: 'POST', body: JSON.stringify(items) })110}, 500)111112// Only use onUpdated for DOM synchronization113onUpdated(() => {114// Scroll to bottom only if content changed height115if (scrollContainer.value) {116scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight117}118})119</script>120```121122```javascript123// GOOD: Conditional check in updated hook124export default {125data() {126return {127content: '',128lastSyncedContent: ''129}130},131updated() {132// Only act if specific condition is met133if (this.content !== this.lastSyncedContent) {134this.syncContent()135this.lastSyncedContent = this.content136}137},138methods: {139syncContent: debounce(function() {140// Sync logic141}, 300)142}143}144```145146## Valid Use Cases for Updated Hook147148```javascript149// GOOD: Low-level DOM synchronization150export default {151updated() {152// Sync third-party library with Vue's DOM153this.thirdPartyWidget.refresh()154155// Update scroll position after content change156this.$nextTick(() => {157this.maintainScrollPosition()158})159}160}161```162163## Prefer Computed Properties for Derived Data164165```javascript166// BAD: Calculating derived data in updated167export default {168data() {169return { numbers: [1, 2, 3, 4, 5] }170},171updated() {172this.sum = this.numbers.reduce((a, b) => a + b, 0) // Causes another update!173}174}175176// GOOD: Use computed property instead177export default {178data() {179return { numbers: [1, 2, 3, 4, 5] }180},181computed: {182sum() {183return this.numbers.reduce((a, b) => a + b, 0)184}185}186}187```188