--- id: wiki-2026-0508-computed-properties-watchers title: Computed Properties & Watchers category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Vue computed, Vue watch, reactive-derivations] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [vue, reactivity, composition-api, computed, watch] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: vue3 --- # Computed Properties & Watchers ## 매 한 줄 > **"매 derived state 의 declarative cache (`computed`), 매 side-effect 의 explicit subscription (`watch`)"**. 매 Vue 3.5 (2024) reactive system 의 두 축. 매 computed 는 pull-based memoization, watch 는 push-based callback. ## 매 핵심 ### 매 computed 의 본질 - 매 dependency 의 자동 추적 → 매 read 시점 lazy evaluation. - 매 동일 input 의 cached return — Object.is 비교. - 매 ref 처럼 `.value` 의 unwrap. - 매 writable computed 의 setter 정의 가능. ### 매 watch / watchEffect 의 차이 - `watch(source, cb)`: 매 explicit source — 매 old/new value 의 access. - `watchEffect(fn)`: 매 자동 dep tracking — 매 deps mutation 시 fn 의 re-run. - 매 flush timing: `'pre'` (default, before render) / `'post'` (after DOM) / `'sync'` (즉시). ### 매 Vue 3.5 reactivity 개선 - 매 `computed` 의 lazy invalidation — 매 unused chain 의 skip. - 매 SSR friendly — server 에서 watch 의 no-op. - 매 onWatcherCleanup() 의 자동 cleanup hook. ## 💻 패턴 ### Basic computed ```typescript import { ref, computed } from 'vue'; const firstName = ref('Yuna'); const lastName = ref('Kim'); const fullName = computed(() => `${firstName.value} ${lastName.value}`); console.log(fullName.value); // "Yuna Kim" — cached firstName.value = 'Jihoon'; console.log(fullName.value); // 매 invalidate → recompute ``` ### Writable computed ```typescript const count = ref(0); const double = computed({ get: () => count.value * 2, set: (v) => { count.value = v / 2; }, }); double.value = 10; // count.value === 5 ``` ### watch (explicit source) ```typescript import { ref, watch } from 'vue'; const userId = ref(1); const user = ref(null); watch(userId, async (id, oldId, onCleanup) => { const ctrl = new AbortController(); onCleanup(() => ctrl.abort()); // 매 stale request 의 cancel user.value = await fetchUser(id, { signal: ctrl.signal }); }, { immediate: true }); ``` ### watchEffect (auto-track) ```typescript import { ref, watchEffect } from 'vue'; const query = ref(''); const results = ref([]); watchEffect(async (onCleanup) => { const ctrl = new AbortController(); onCleanup(() => ctrl.abort()); results.value = await searchAPI(query.value, { signal: ctrl.signal }); }); ``` ### Deep watch (object/array) ```typescript const filters = reactive({ q: '', tags: [] }); watch(filters, (next) => { console.log('filters changed', JSON.parse(JSON.stringify(next))); }, { deep: true }); ``` ### Multiple sources ```typescript const x = ref(0), y = ref(0); watch([x, y], ([nx, ny], [ox, oy]) => { console.log(`(${ox},${oy}) → (${nx},${ny})`); }); ``` ### Flush timing — DOM 접근 ```typescript const list = ref([]); const containerRef = ref(); watch(list, () => { // 매 DOM 의 update 후 scrollTop 의 read containerRef.value!.scrollTop = containerRef.value!.scrollHeight; }, { flush: 'post' }); ``` ## 매 결정 기준 | 상황 | Approach | |---|---| | 매 derived value (template render) | `computed` | | 매 expensive computation + cache | `computed` | | 매 external side-effect (fetch, DOM) | `watch` / `watchEffect` | | 매 conditional dep tracking | `watch` (explicit source) | | 매 모든 reactive read 의 react | `watchEffect` | **기본값**: 매 derive 는 `computed`, 매 effect 는 `watch` with explicit source. ## 🔗 Graph - 부모: [[Composition API]] · [[Vue_Single-File_Components_SFC]] - 변형: [[Composables]] · [[Options API]] - 응용: [[Pinia]] · [[Server_State_Management]] - Adjacent: [[State_Management]] ## 🤖 LLM 활용 **언제**: 매 Vue 3 component 의 derived data 정의, 매 async data fetch 의 dependency 변경 추적, 매 form-validation reactive chain. **언제 X**: 매 React/Solid 의 코드 (각 framework 의 hook/signal 사용), 매 single-shot computation (그냥 함수). ## ❌ 안티패턴 - **매 computed side-effect**: 매 getter 안에서 mutation/fetch — 매 cache invalidation 의 chaos. - **매 watch deep 의 남용**: 매 large object deep watch — 매 expensive equality check. - **매 immediate + cleanup 누락**: 매 첫 fetch 의 race — 매 onCleanup 의 mandatory. - **매 watchEffect 의 conditional dep**: 매 첫 run 에서 안 읽힌 ref 의 untracked. ## 🧪 검증 / 중복 - Verified (Vue 3.5 docs vuejs.org/api/reactivity-core, Evan You blog 2024). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — computed/watch 의 timing/cleanup 패턴 정리 |