@pythoughts/vue-skills-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/index.mjs +139 -0
- package/package.json +34 -0
- package/skills/create-adaptable-composable/SKILL.md +76 -0
- package/skills/vue-best-practices/SKILL.md +154 -0
- package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
- package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
- package/skills/vue-best-practices/references/component-async.md +97 -0
- package/skills/vue-best-practices/references/component-data-flow.md +350 -0
- package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
- package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
- package/skills/vue-best-practices/references/component-slots.md +216 -0
- package/skills/vue-best-practices/references/component-suspense.md +228 -0
- package/skills/vue-best-practices/references/component-teleport.md +108 -0
- package/skills/vue-best-practices/references/component-transition-group.md +128 -0
- package/skills/vue-best-practices/references/component-transition.md +125 -0
- package/skills/vue-best-practices/references/composables.md +290 -0
- package/skills/vue-best-practices/references/directives.md +162 -0
- package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
- package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
- package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
- package/skills/vue-best-practices/references/plugins.md +166 -0
- package/skills/vue-best-practices/references/reactivity.md +346 -0
- package/skills/vue-best-practices/references/render-functions.md +201 -0
- package/skills/vue-best-practices/references/sfc.md +310 -0
- package/skills/vue-best-practices/references/state-management.md +135 -0
- package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
- package/skills/vue-debug-guides/SKILL.md +205 -0
- package/skills/vue-debug-guides/reference/animation-key-for-rerender.md +160 -0
- package/skills/vue-debug-guides/reference/animation-transitiongroup-performance.md +241 -0
- package/skills/vue-debug-guides/reference/async-component-error-handling.md +115 -0
- package/skills/vue-debug-guides/reference/async-component-keepalive-ref-issue.md +112 -0
- package/skills/vue-debug-guides/reference/async-component-suspense-control.md +84 -0
- package/skills/vue-debug-guides/reference/async-component-vue-router.md +109 -0
- package/skills/vue-debug-guides/reference/attrs-event-listener-merging.md +205 -0
- package/skills/vue-debug-guides/reference/checkbox-true-false-value-form-submission.md +118 -0
- package/skills/vue-debug-guides/reference/cleanup-side-effects.md +172 -0
- package/skills/vue-debug-guides/reference/click-events-on-components.md +180 -0
- package/skills/vue-debug-guides/reference/component-naming-conflicts.md +159 -0
- package/skills/vue-debug-guides/reference/component-ref-requires-defineexpose.md +176 -0
- package/skills/vue-debug-guides/reference/composable-avoid-hidden-side-effects.md +208 -0
- package/skills/vue-debug-guides/reference/composable-call-location-restrictions.md +141 -0
- package/skills/vue-debug-guides/reference/composable-naming-return-pattern.md +139 -0
- package/skills/vue-debug-guides/reference/composable-tovalue-inside-watcheffect.md +182 -0
- package/skills/vue-debug-guides/reference/composition-api-not-functional-programming.md +120 -0
- package/skills/vue-debug-guides/reference/composition-api-script-setup-async-context.md +203 -0
- package/skills/vue-debug-guides/reference/composition-api-vs-react-hooks-differences.md +156 -0
- package/skills/vue-debug-guides/reference/computed-array-mutation.md +148 -0
- package/skills/vue-debug-guides/reference/computed-conditional-dependencies.md +147 -0
- package/skills/vue-debug-guides/reference/computed-no-parameters.md +159 -0
- package/skills/vue-debug-guides/reference/computed-no-side-effects.md +107 -0
- package/skills/vue-debug-guides/reference/computed-return-value-readonly.md +160 -0
- package/skills/vue-debug-guides/reference/configure-app-before-mount.md +89 -0
- package/skills/vue-debug-guides/reference/declare-emits-for-documentation.md +212 -0
- package/skills/vue-debug-guides/reference/define-expose-before-await.md +192 -0
- package/skills/vue-debug-guides/reference/define-model-default-value-sync.md +139 -0
- package/skills/vue-debug-guides/reference/defineEmits-must-be-top-level.md +164 -0
- package/skills/vue-debug-guides/reference/defineEmits-no-runtime-and-type-mixed.md +170 -0
- package/skills/vue-debug-guides/reference/definemodel-object-mutation-no-emit.md +148 -0
- package/skills/vue-debug-guides/reference/dom-update-timing-nexttick.md +90 -0
- package/skills/vue-debug-guides/reference/dynamic-argument-constraints.md +146 -0
- package/skills/vue-debug-guides/reference/dynamic-component-registration-vite.md +147 -0
- package/skills/vue-debug-guides/reference/event-modifier-order-matters.md +101 -0
- package/skills/vue-debug-guides/reference/exact-modifier-for-precise-shortcuts.md +155 -0
- package/skills/vue-debug-guides/reference/fallthrough-attrs-overwrite-vue3.md +159 -0
- package/skills/vue-debug-guides/reference/in-dom-template-parsing-caveats.md +149 -0
- package/skills/vue-debug-guides/reference/inheritattrs-false-for-wrapper-components.md +230 -0
- package/skills/vue-debug-guides/reference/keepalive-router-nested-double-mount.md +222 -0
- package/skills/vue-debug-guides/reference/keepalive-transition-memory-leak.md +144 -0
- package/skills/vue-debug-guides/reference/keyup-modifier-timing.md +137 -0
- package/skills/vue-debug-guides/reference/lifecycle-dom-access-timing.md +216 -0
- package/skills/vue-debug-guides/reference/lifecycle-hooks-synchronous-registration.md +156 -0
- package/skills/vue-debug-guides/reference/lifecycle-ssr-awareness.md +184 -0
- package/skills/vue-debug-guides/reference/local-components-not-in-descendants.md +151 -0
- package/skills/vue-debug-guides/reference/mount-return-value.md +88 -0
- package/skills/vue-debug-guides/reference/multi-root-component-class-attrs.md +93 -0
- package/skills/vue-debug-guides/reference/native-event-collision-with-emits.md +162 -0
- package/skills/vue-debug-guides/reference/no-passive-with-prevent.md +141 -0
- package/skills/vue-debug-guides/reference/no-v-if-with-v-for.md +136 -0
- package/skills/vue-debug-guides/reference/perf-computed-object-stability.md +157 -0
- package/skills/vue-debug-guides/reference/perf-props-stability-update-optimization.md +140 -0
- package/skills/vue-debug-guides/reference/plugin-global-properties-sparingly.md +109 -0
- package/skills/vue-debug-guides/reference/plugin-install-before-mount.md +124 -0
- package/skills/vue-debug-guides/reference/plugin-prefer-provide-inject-over-global-properties.md +120 -0
- package/skills/vue-debug-guides/reference/plugin-typescript-type-augmentation.md +157 -0
- package/skills/vue-debug-guides/reference/prop-defineprops-scope-limitation.md +161 -0
- package/skills/vue-debug-guides/reference/provide-inject-debugging-challenges.md +203 -0
- package/skills/vue-debug-guides/reference/provide-inject-default-value-factory.md +244 -0
- package/skills/vue-debug-guides/reference/provide-inject-reactivity-not-automatic.md +226 -0
- package/skills/vue-debug-guides/reference/provide-inject-synchronous-setup.md +235 -0
- package/skills/vue-debug-guides/reference/reactive-destructuring.md +89 -0
- package/skills/vue-debug-guides/reference/reactivity-debugging-hooks.md +132 -0
- package/skills/vue-debug-guides/reference/reactivity-markraw-for-non-reactive.md +149 -0
- package/skills/vue-debug-guides/reference/reactivity-proxy-identity-hazard.md +96 -0
- package/skills/vue-debug-guides/reference/reactivity-same-tick-batching.md +166 -0
- package/skills/vue-debug-guides/reference/ref-value-access.md +61 -0
- package/skills/vue-debug-guides/reference/refs-in-collections-need-value.md +81 -0
- package/skills/vue-debug-guides/reference/render-function-avoid-internal-vnode-properties.md +151 -0
- package/skills/vue-debug-guides/reference/render-function-vnodes-must-be-unique.md +133 -0
- package/skills/vue-debug-guides/reference/rendering-render-function-h-import-vue3.md +148 -0
- package/skills/vue-debug-guides/reference/rendering-render-function-return-from-setup.md +148 -0
- package/skills/vue-debug-guides/reference/rendering-render-function-slots-as-functions.md +168 -0
- package/skills/vue-debug-guides/reference/rendering-resolve-component-for-string-names.md +231 -0
- package/skills/vue-debug-guides/reference/select-initial-value-ios-bug.md +91 -0
- package/skills/vue-debug-guides/reference/self-referencing-component-name.md +157 -0
- package/skills/vue-debug-guides/reference/sfc-named-exports-forbidden.md +184 -0
- package/skills/vue-debug-guides/reference/sfc-scoped-css-child-component-styling.md +156 -0
- package/skills/vue-debug-guides/reference/sfc-scoped-css-dynamic-content.md +193 -0
- package/skills/vue-debug-guides/reference/sfc-scoped-css-slot-content.md +242 -0
- package/skills/vue-debug-guides/reference/sfc-script-setup-reactivity.md +195 -0
- package/skills/vue-debug-guides/reference/slot-forwarding-to-child-components.md +143 -0
- package/skills/vue-debug-guides/reference/slot-implicit-default-content.md +155 -0
- package/skills/vue-debug-guides/reference/slot-name-reserved-prop.md +109 -0
- package/skills/vue-debug-guides/reference/slot-named-scoped-explicit-default.md +95 -0
- package/skills/vue-debug-guides/reference/slot-render-scope-parent-only.md +135 -0
- package/skills/vue-debug-guides/reference/slot-v-slot-on-components-or-templates-only.md +122 -0
- package/skills/vue-debug-guides/reference/ssr-hydration-mismatch-causes.md +280 -0
- package/skills/vue-debug-guides/reference/ssr-platform-specific-apis.md +256 -0
- package/skills/vue-debug-guides/reference/state-ssr-cross-request-pollution.md +276 -0
- package/skills/vue-debug-guides/reference/suspense-no-builtin-error-handling.md +127 -0
- package/skills/vue-debug-guides/reference/suspense-ssr-hydration-issues.md +159 -0
- package/skills/vue-debug-guides/reference/tailwind-dynamic-class-generation.md +144 -0
- package/skills/vue-debug-guides/reference/teleport-scoped-styles-limitation.md +191 -0
- package/skills/vue-debug-guides/reference/teleport-ssr-hydration.md +152 -0
- package/skills/vue-debug-guides/reference/teleport-target-must-exist.md +113 -0
- package/skills/vue-debug-guides/reference/template-expressions-restrictions.md +114 -0
- package/skills/vue-debug-guides/reference/template-functions-no-side-effects.md +187 -0
- package/skills/vue-debug-guides/reference/template-ref-null-with-v-if.md +123 -0
- package/skills/vue-debug-guides/reference/template-ref-unwrapping-top-level.md +104 -0
- package/skills/vue-debug-guides/reference/template-ref-v-for-order.md +172 -0
- package/skills/vue-debug-guides/reference/textarea-no-interpolation.md +72 -0
- package/skills/vue-debug-guides/reference/transition-group-flip-inline-elements.md +152 -0
- package/skills/vue-debug-guides/reference/transition-group-move-animation-position-absolute.md +130 -0
- package/skills/vue-debug-guides/reference/transition-group-no-default-wrapper-vue3.md +152 -0
- package/skills/vue-debug-guides/reference/transition-js-hooks-done-callback.md +251 -0
- package/skills/vue-debug-guides/reference/transition-nested-duration.md +182 -0
- package/skills/vue-debug-guides/reference/transition-reusable-scoped-style.md +245 -0
- package/skills/vue-debug-guides/reference/transition-router-view-appear.md +193 -0
- package/skills/vue-debug-guides/reference/transition-type-when-mixed.md +172 -0
- package/skills/vue-debug-guides/reference/transition-unmount-hook-timing.md +149 -0
- package/skills/vue-debug-guides/reference/ts-defineprops-boolean-default-false.md +225 -0
- package/skills/vue-debug-guides/reference/ts-defineprops-imported-types-limitations.md +281 -0
- package/skills/vue-debug-guides/reference/ts-event-handler-explicit-typing.md +213 -0
- package/skills/vue-debug-guides/reference/ts-reactive-no-generic-argument.md +196 -0
- package/skills/vue-debug-guides/reference/ts-shallowref-for-dynamic-components.md +218 -0
- package/skills/vue-debug-guides/reference/ts-template-ref-null-handling.md +249 -0
- package/skills/vue-debug-guides/reference/ts-template-type-casting.md +214 -0
- package/skills/vue-debug-guides/reference/ts-withdefaults-mutable-factory-function.md +171 -0
- package/skills/vue-debug-guides/reference/undeclared-emits-double-firing.md +195 -0
- package/skills/vue-debug-guides/reference/use-template-ref-vue35.md +158 -0
- package/skills/vue-debug-guides/reference/v-else-must-follow-v-if.md +136 -0
- package/skills/vue-debug-guides/reference/v-for-component-props.md +95 -0
- package/skills/vue-debug-guides/reference/v-for-computed-reverse-sort.md +86 -0
- package/skills/vue-debug-guides/reference/v-for-key-attribute.md +90 -0
- package/skills/vue-debug-guides/reference/v-for-range-starts-at-one.md +66 -0
- package/skills/vue-debug-guides/reference/v-if-null-check-order.md +171 -0
- package/skills/vue-debug-guides/reference/v-model-ignores-html-attributes.md +83 -0
- package/skills/vue-debug-guides/reference/v-model-ime-composition.md +83 -0
- package/skills/vue-debug-guides/reference/v-model-number-modifier-behavior.md +124 -0
- package/skills/vue-debug-guides/reference/v-show-template-limitation.md +124 -0
- package/skills/vue-debug-guides/reference/watch-async-cleanup.md +180 -0
- package/skills/vue-debug-guides/reference/watch-async-creation-memory-leak.md +176 -0
- package/skills/vue-debug-guides/reference/watch-deep-same-object-reference.md +165 -0
- package/skills/vue-debug-guides/reference/watch-flush-timing.md +189 -0
- package/skills/vue-debug-guides/reference/watch-reactive-property-getter.md +108 -0
- package/skills/vue-debug-guides/reference/watcheffect-async-dependency-tracking.md +173 -0
- package/skills/vue-debug-guides/reference/watcheffect-flush-post-for-refs.md +176 -0
- package/skills/vue-jsx-best-practices/SKILL.md +12 -0
- package/skills/vue-jsx-best-practices/reference/render-function-jsx-vue-vs-react.md +141 -0
- package/skills/vue-options-api-best-practices/SKILL.md +23 -0
- package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-lifecycle-hooks.md +95 -0
- package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-methods.md +68 -0
- package/skills/vue-options-api-best-practices/reference/stateful-methods-lifecycle.md +61 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-arrow-functions-validators.md +141 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-computed-return-types.md +192 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-proptype-complex-types.md +212 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-provide-inject-limitations.md +135 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-type-event-handlers.md +202 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-use-definecomponent.md +172 -0
- package/skills/vue-options-api-best-practices/reference/ts-strict-mode-options-api.md +197 -0
- package/skills/vue-pinia-best-practices/SKILL.md +21 -0
- package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
- package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
- package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
- package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
- package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
- package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
- package/skills/vue-router-best-practices/SKILL.md +23 -0
- package/skills/vue-router-best-practices/reference/router-beforeenter-no-param-trigger.md +167 -0
- package/skills/vue-router-best-practices/reference/router-beforerouteenter-no-this.md +176 -0
- package/skills/vue-router-best-practices/reference/router-guard-async-await-pattern.md +227 -0
- package/skills/vue-router-best-practices/reference/router-navigation-guard-infinite-loop.md +187 -0
- package/skills/vue-router-best-practices/reference/router-navigation-guard-next-deprecated.md +150 -0
- package/skills/vue-router-best-practices/reference/router-param-change-no-lifecycle.md +181 -0
- package/skills/vue-router-best-practices/reference/router-simple-routing-cleanup.md +209 -0
- package/skills/vue-router-best-practices/reference/router-use-vue-router-for-production.md +183 -0
- package/skills/vue-testing-best-practices/SKILL.md +29 -0
- package/skills/vue-testing-best-practices/reference/async-component-testing.md +163 -0
- package/skills/vue-testing-best-practices/reference/teleport-testing-complexity.md +158 -0
- package/skills/vue-testing-best-practices/reference/testing-async-await-flushpromises.md +175 -0
- package/skills/vue-testing-best-practices/reference/testing-browser-vs-node-runners.md +208 -0
- package/skills/vue-testing-best-practices/reference/testing-component-blackbox-approach.md +144 -0
- package/skills/vue-testing-best-practices/reference/testing-composables-helper-wrapper.md +238 -0
- package/skills/vue-testing-best-practices/reference/testing-e2e-playwright-recommended.md +242 -0
- package/skills/vue-testing-best-practices/reference/testing-no-snapshot-only.md +197 -0
- package/skills/vue-testing-best-practices/reference/testing-pinia-store-setup.md +228 -0
- package/skills/vue-testing-best-practices/reference/testing-suspense-async-components.md +229 -0
- package/skills/vue-testing-best-practices/reference/testing-vitest-recommended-for-vue.md +204 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Deep Watch Callback Receives Same Object Reference for Old and New Values
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Comparing oldValue and newValue in deep watchers is misleading since they reference the same object
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, watch, watchers, deep, oldValue, newValue, object-reference]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Deep Watch Callback Receives Same Object Reference for Old and New Values
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - When using deep watchers on reactive objects, both `newValue` and `oldValue` in the callback point to the same object reference. They will always be equal for nested mutations because Vue doesn't clone the object before mutation.
|
|
12
|
+
|
|
13
|
+
Don't rely on comparing `newValue` to `oldValue` in deep watchers for detecting what changed. Instead, track specific values or implement your own diffing.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Don't compare newValue === oldValue in deep watchers to detect changes
|
|
18
|
+
- [ ] For change detection, watch specific properties instead
|
|
19
|
+
- [ ] If you need old values, manually snapshot before changes
|
|
20
|
+
- [ ] Consider using a serialization approach for complex diffing needs
|
|
21
|
+
- [ ] The values differ only when the entire object is replaced
|
|
22
|
+
|
|
23
|
+
**Incorrect:**
|
|
24
|
+
```javascript
|
|
25
|
+
import { reactive, watch } from 'vue'
|
|
26
|
+
|
|
27
|
+
const state = reactive({
|
|
28
|
+
user: {
|
|
29
|
+
name: 'John',
|
|
30
|
+
preferences: { theme: 'dark' }
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// BAD: Trying to compare old and new values
|
|
35
|
+
watch(
|
|
36
|
+
() => state.user,
|
|
37
|
+
(newUser, oldUser) => {
|
|
38
|
+
// This comparison is ALWAYS true for nested mutations!
|
|
39
|
+
if (newUser === oldUser) {
|
|
40
|
+
console.log('Same reference!') // Always logs for nested changes
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// This also won't work - they're the same object
|
|
44
|
+
if (newUser.name !== oldUser.name) {
|
|
45
|
+
console.log('Name changed') // Never logs for nested mutations
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{ deep: true }
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
// When this happens:
|
|
52
|
+
state.user.name = 'Jane'
|
|
53
|
+
// Both newUser and oldUser are { name: 'Jane', preferences: { theme: 'dark' } }
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Correct:**
|
|
57
|
+
```javascript
|
|
58
|
+
import { reactive, watch, ref } from 'vue'
|
|
59
|
+
|
|
60
|
+
const state = reactive({
|
|
61
|
+
user: {
|
|
62
|
+
name: 'John',
|
|
63
|
+
preferences: { theme: 'dark' }
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// CORRECT: Watch specific properties you care about
|
|
68
|
+
watch(
|
|
69
|
+
() => state.user.name,
|
|
70
|
+
(newName, oldName) => {
|
|
71
|
+
console.log(`Name changed from "${oldName}" to "${newName}"`)
|
|
72
|
+
// oldName and newName are primitives, work correctly
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
// CORRECT: Watch multiple specific properties
|
|
77
|
+
watch(
|
|
78
|
+
[() => state.user.name, () => state.user.preferences.theme],
|
|
79
|
+
([newName, newTheme], [oldName, oldTheme]) => {
|
|
80
|
+
if (newName !== oldName) {
|
|
81
|
+
console.log(`Name: ${oldName} -> ${newName}`)
|
|
82
|
+
}
|
|
83
|
+
if (newTheme !== oldTheme) {
|
|
84
|
+
console.log(`Theme: ${oldTheme} -> ${newTheme}`)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Manual Snapshot Pattern
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
import { reactive, watch, ref } from 'vue'
|
|
94
|
+
|
|
95
|
+
const state = reactive({ count: 0, items: [] })
|
|
96
|
+
|
|
97
|
+
// Keep a manual snapshot for comparison
|
|
98
|
+
const previousSnapshot = ref(JSON.stringify(state))
|
|
99
|
+
|
|
100
|
+
watch(
|
|
101
|
+
state,
|
|
102
|
+
(newState) => {
|
|
103
|
+
const currentSnapshot = JSON.stringify(newState)
|
|
104
|
+
|
|
105
|
+
if (currentSnapshot !== previousSnapshot.value) {
|
|
106
|
+
const oldData = JSON.parse(previousSnapshot.value)
|
|
107
|
+
console.log('Old:', oldData)
|
|
108
|
+
console.log('New:', newState)
|
|
109
|
+
|
|
110
|
+
// Update snapshot for next comparison
|
|
111
|
+
previousSnapshot.value = currentSnapshot
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{ deep: true }
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## When Old and New Values Differ
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
import { reactive, watch } from 'vue'
|
|
122
|
+
|
|
123
|
+
const state = reactive({
|
|
124
|
+
currentUser: { name: 'John' }
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
watch(
|
|
128
|
+
() => state.currentUser,
|
|
129
|
+
(newUser, oldUser) => {
|
|
130
|
+
// THESE DIFFER when the object itself is replaced
|
|
131
|
+
console.log('Old:', oldUser) // { name: 'John' }
|
|
132
|
+
console.log('New:', newUser) // { name: 'Jane' }
|
|
133
|
+
},
|
|
134
|
+
{ deep: true }
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
// Object replacement - old and new are different
|
|
138
|
+
state.currentUser = { name: 'Jane' }
|
|
139
|
+
|
|
140
|
+
// vs. Mutation - old and new are the same reference
|
|
141
|
+
// state.currentUser.name = 'Jane'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Using Getter Returns New Object
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
import { reactive, watch } from 'vue'
|
|
148
|
+
|
|
149
|
+
const state = reactive({
|
|
150
|
+
user: { firstName: 'John', lastName: 'Doe' }
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
// CORRECT: Getter returns new object, so old/new comparison works
|
|
154
|
+
watch(
|
|
155
|
+
() => ({ ...state.user }), // Shallow clone
|
|
156
|
+
(newUser, oldUser) => {
|
|
157
|
+
// Now these are different objects
|
|
158
|
+
console.log('Changed from', oldUser, 'to', newUser)
|
|
159
|
+
},
|
|
160
|
+
{ deep: true }
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Reference
|
|
165
|
+
- [Vue.js Watchers - Deep Watchers](https://vuejs.org/guide/essentials/watchers.html#deep-watchers)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use flush post When Accessing Updated DOM in Watchers
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Default watcher timing runs before DOM updates, causing stale DOM reads
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, watch, watchers, flush, DOM, timing, post]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Use flush: 'post' When Accessing Updated DOM in Watchers
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - By default, watcher callbacks run before the component's DOM is updated. If you access the DOM in a watcher callback, you'll see the pre-update state. Use `flush: 'post'` or `watchPostEffect` when you need to access the updated DOM.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Use `{ flush: 'post' }` when reading DOM after reactive state changes
|
|
16
|
+
- [ ] Use `watchPostEffect()` as a shorthand for `watchEffect` with flush: 'post'
|
|
17
|
+
- [ ] Avoid `{ flush: 'sync' }` unless absolutely necessary (performance impact)
|
|
18
|
+
- [ ] Remember default timing is ideal for most non-DOM operations
|
|
19
|
+
|
|
20
|
+
**Incorrect:**
|
|
21
|
+
```vue
|
|
22
|
+
<script setup>
|
|
23
|
+
import { ref, watch, watchEffect } from 'vue'
|
|
24
|
+
|
|
25
|
+
const count = ref(0)
|
|
26
|
+
const listItems = ref(['a', 'b', 'c'])
|
|
27
|
+
|
|
28
|
+
// BAD: DOM shows old value when this runs
|
|
29
|
+
watch(count, () => {
|
|
30
|
+
// Element still shows the OLD count value
|
|
31
|
+
const el = document.querySelector('.counter')
|
|
32
|
+
console.log('DOM shows:', el.textContent) // Old value!
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// BAD: List DOM not yet updated
|
|
36
|
+
watchEffect(() => {
|
|
37
|
+
console.log('Items:', listItems.value.length)
|
|
38
|
+
// DOM still has old number of list items
|
|
39
|
+
const items = document.querySelectorAll('.list-item')
|
|
40
|
+
console.log('DOM items:', items.length) // Old count!
|
|
41
|
+
})
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<template>
|
|
45
|
+
<div class="counter">{{ count }}</div>
|
|
46
|
+
<ul>
|
|
47
|
+
<li v-for="item in listItems" :key="item" class="list-item">
|
|
48
|
+
{{ item }}
|
|
49
|
+
</li>
|
|
50
|
+
</ul>
|
|
51
|
+
</template>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Correct:**
|
|
55
|
+
```vue
|
|
56
|
+
<script setup>
|
|
57
|
+
import { ref, watch, watchEffect, watchPostEffect } from 'vue'
|
|
58
|
+
|
|
59
|
+
const count = ref(0)
|
|
60
|
+
const listItems = ref(['a', 'b', 'c'])
|
|
61
|
+
|
|
62
|
+
// CORRECT: flush: 'post' runs after DOM update
|
|
63
|
+
watch(
|
|
64
|
+
count,
|
|
65
|
+
() => {
|
|
66
|
+
const el = document.querySelector('.counter')
|
|
67
|
+
console.log('DOM shows:', el.textContent) // Correct new value!
|
|
68
|
+
},
|
|
69
|
+
{ flush: 'post' }
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
// CORRECT: watchPostEffect shorthand
|
|
73
|
+
watchPostEffect(() => {
|
|
74
|
+
console.log('Items:', listItems.value.length)
|
|
75
|
+
const items = document.querySelectorAll('.list-item')
|
|
76
|
+
console.log('DOM items:', items.length) // Matches listItems.length!
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// CORRECT: Using watchEffect with flush option
|
|
80
|
+
watchEffect(
|
|
81
|
+
() => {
|
|
82
|
+
// Access reactive state and DOM together
|
|
83
|
+
const expectedCount = listItems.value.length
|
|
84
|
+
const actualCount = document.querySelectorAll('.list-item').length
|
|
85
|
+
console.log(`Expected: ${expectedCount}, Actual: ${actualCount}`)
|
|
86
|
+
},
|
|
87
|
+
{ flush: 'post' }
|
|
88
|
+
)
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<template>
|
|
92
|
+
<div class="counter">{{ count }}</div>
|
|
93
|
+
<ul>
|
|
94
|
+
<li v-for="item in listItems" :key="item" class="list-item">
|
|
95
|
+
{{ item }}
|
|
96
|
+
</li>
|
|
97
|
+
</ul>
|
|
98
|
+
</template>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Flush Timing Options
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
import { watch, watchEffect, watchPostEffect, watchSyncEffect } from 'vue'
|
|
105
|
+
|
|
106
|
+
// Default: 'pre' - runs before component DOM update
|
|
107
|
+
watch(source, callback) // Same as { flush: 'pre' }
|
|
108
|
+
|
|
109
|
+
// Post: runs after component DOM update
|
|
110
|
+
watch(source, callback, { flush: 'post' })
|
|
111
|
+
watchPostEffect(callback) // Shorthand
|
|
112
|
+
|
|
113
|
+
// Sync: runs immediately when reactive value changes
|
|
114
|
+
// USE WITH CAUTION - no batching, fires on every mutation
|
|
115
|
+
watch(source, callback, { flush: 'sync' })
|
|
116
|
+
watchSyncEffect(callback) // Shorthand
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## When to Use Each Flush Timing
|
|
120
|
+
|
|
121
|
+
| Timing | Use Case |
|
|
122
|
+
|--------|----------|
|
|
123
|
+
| `'pre'` (default) | Logic that doesn't need DOM access |
|
|
124
|
+
| `'post'` | Reading or measuring updated DOM |
|
|
125
|
+
| `'sync'` | Debug logging, simple boolean flags only |
|
|
126
|
+
|
|
127
|
+
## Sync Watcher Warning
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
import { ref, watch } from 'vue'
|
|
131
|
+
|
|
132
|
+
const items = ref([1, 2, 3])
|
|
133
|
+
|
|
134
|
+
// DANGEROUS: Fires for EVERY array mutation
|
|
135
|
+
watch(
|
|
136
|
+
items,
|
|
137
|
+
() => {
|
|
138
|
+
console.log('Changed!') // Called 3 times for push, push, push
|
|
139
|
+
},
|
|
140
|
+
{ flush: 'sync' }
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
// This triggers the watcher 3 times synchronously
|
|
144
|
+
items.value.push(4)
|
|
145
|
+
items.value.push(5)
|
|
146
|
+
items.value.push(6)
|
|
147
|
+
|
|
148
|
+
// Better: Use default flush which batches updates
|
|
149
|
+
watch(items, () => {
|
|
150
|
+
console.log('Changed!') // Called once after all mutations
|
|
151
|
+
}, { deep: true })
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Practical Example: Auto-scroll
|
|
155
|
+
|
|
156
|
+
```vue
|
|
157
|
+
<script setup>
|
|
158
|
+
import { ref, watchPostEffect } from 'vue'
|
|
159
|
+
|
|
160
|
+
const messages = ref([])
|
|
161
|
+
const containerRef = ref(null)
|
|
162
|
+
|
|
163
|
+
// Auto-scroll to bottom when new messages arrive
|
|
164
|
+
watchPostEffect(() => {
|
|
165
|
+
// Access messages.value to track it
|
|
166
|
+
const msgCount = messages.value.length
|
|
167
|
+
|
|
168
|
+
// DOM is updated, safe to scroll
|
|
169
|
+
if (containerRef.value && msgCount > 0) {
|
|
170
|
+
containerRef.value.scrollTop = containerRef.value.scrollHeight
|
|
171
|
+
}
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
function addMessage(text) {
|
|
175
|
+
messages.value.push({ text, timestamp: Date.now() })
|
|
176
|
+
}
|
|
177
|
+
</script>
|
|
178
|
+
|
|
179
|
+
<template>
|
|
180
|
+
<div ref="containerRef" class="messages">
|
|
181
|
+
<div v-for="msg in messages" :key="msg.timestamp">
|
|
182
|
+
{{ msg.text }}
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</template>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Reference
|
|
189
|
+
- [Vue.js Watchers - Callback Flush Timing](https://vuejs.org/guide/essentials/watchers.html#callback-flush-timing)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Getter Function When Watching Reactive Object Properties
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Watching reactive properties directly passes a primitive value, causing the watcher to never trigger
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, watch, watchers, reactive, getter, common-mistake]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Use Getter Function When Watching Reactive Object Properties
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - Directly watching a property of a reactive object passes a primitive value to `watch()`, not a reactive reference. The watcher will never trigger because primitives are not reactive.
|
|
12
|
+
|
|
13
|
+
When you need to watch a specific property of a reactive object, always wrap it in a getter function `() => obj.property`.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Always use getter functions when watching properties of reactive objects
|
|
18
|
+
- [ ] Remember that `watch(obj.count, ...)` passes the current value, not a reactive reference
|
|
19
|
+
- [ ] For refs, you can watch directly: `watch(myRef, ...)`
|
|
20
|
+
- [ ] For entire reactive objects, you can watch directly (creates implicit deep watcher)
|
|
21
|
+
|
|
22
|
+
**Incorrect:**
|
|
23
|
+
```javascript
|
|
24
|
+
import { reactive, watch } from 'vue'
|
|
25
|
+
|
|
26
|
+
const state = reactive({ count: 0, name: 'Vue' })
|
|
27
|
+
|
|
28
|
+
// WRONG: Passes the number 0 to watch(), not a reactive reference
|
|
29
|
+
// This watcher will NEVER fire!
|
|
30
|
+
watch(state.count, (newCount) => {
|
|
31
|
+
console.log(`Count changed to: ${newCount}`)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// WRONG: Same problem with string property
|
|
35
|
+
watch(state.name, (newName) => {
|
|
36
|
+
console.log(`Name changed to: ${newName}`)
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Correct:**
|
|
41
|
+
```javascript
|
|
42
|
+
import { reactive, watch } from 'vue'
|
|
43
|
+
|
|
44
|
+
const state = reactive({ count: 0, name: 'Vue' })
|
|
45
|
+
|
|
46
|
+
// CORRECT: Use a getter function
|
|
47
|
+
watch(
|
|
48
|
+
() => state.count,
|
|
49
|
+
(newCount, oldCount) => {
|
|
50
|
+
console.log(`Count changed from ${oldCount} to ${newCount}`)
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
// CORRECT: Multiple properties with getter
|
|
55
|
+
watch(
|
|
56
|
+
() => state.name,
|
|
57
|
+
(newName) => {
|
|
58
|
+
console.log(`Name changed to: ${newName}`)
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
// CORRECT: Watching derived values
|
|
63
|
+
watch(
|
|
64
|
+
() => state.count * 2,
|
|
65
|
+
(doubledCount) => {
|
|
66
|
+
console.log(`Doubled count: ${doubledCount}`)
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Watching Multiple Properties
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
import { reactive, watch } from 'vue'
|
|
75
|
+
|
|
76
|
+
const state = reactive({ firstName: 'John', lastName: 'Doe' })
|
|
77
|
+
|
|
78
|
+
// Watch multiple properties with array of getters
|
|
79
|
+
watch(
|
|
80
|
+
[() => state.firstName, () => state.lastName],
|
|
81
|
+
([newFirst, newLast], [oldFirst, oldLast]) => {
|
|
82
|
+
console.log(`Name changed from ${oldFirst} ${oldLast} to ${newFirst} ${newLast}`)
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## When Direct Watching Works
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
import { ref, reactive, watch } from 'vue'
|
|
91
|
+
|
|
92
|
+
const count = ref(0)
|
|
93
|
+
const state = reactive({ nested: { value: 1 } })
|
|
94
|
+
|
|
95
|
+
// CORRECT: Refs can be watched directly
|
|
96
|
+
watch(count, (newVal) => {
|
|
97
|
+
console.log(`Count: ${newVal}`)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// CORRECT: Entire reactive objects create implicit deep watcher
|
|
101
|
+
watch(state, (newState) => {
|
|
102
|
+
// Fires on any nested change
|
|
103
|
+
// Note: newState === oldState (same object reference)
|
|
104
|
+
})
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Reference
|
|
108
|
+
- [Vue.js Watchers - Watch Source Types](https://vuejs.org/guide/essentials/watchers.html#watch-source-types)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: watchEffect Only Tracks Dependencies Before First Await
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Dependencies accessed after await are not tracked, causing watchers to miss reactive changes
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, watchEffect, watchers, async, await, dependency-tracking]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# watchEffect Only Tracks Dependencies Before First Await
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - `watchEffect` automatically tracks reactive dependencies, but only during synchronous execution. Any reactive properties accessed after the first `await` statement will NOT be tracked, and changes to them won't trigger the watcher.
|
|
12
|
+
|
|
13
|
+
For async operations, either access all dependencies before the await, or use `watch` with explicit dependencies.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Access all reactive dependencies BEFORE the first await in watchEffect
|
|
18
|
+
- [ ] Use `watch` with explicit source when async tracking is needed
|
|
19
|
+
- [ ] Store reactive values in local variables before await
|
|
20
|
+
- [ ] Be aware that dependencies after await are invisible to Vue
|
|
21
|
+
|
|
22
|
+
**Incorrect:**
|
|
23
|
+
```vue
|
|
24
|
+
<script setup>
|
|
25
|
+
import { ref, watchEffect } from 'vue'
|
|
26
|
+
|
|
27
|
+
const userId = ref(1)
|
|
28
|
+
const includeDetails = ref(true)
|
|
29
|
+
const userData = ref(null)
|
|
30
|
+
|
|
31
|
+
// BAD: includeDetails is accessed after await - NOT TRACKED!
|
|
32
|
+
watchEffect(async () => {
|
|
33
|
+
const response = await fetch(`/api/users/${userId.value}`)
|
|
34
|
+
const data = await response.json()
|
|
35
|
+
|
|
36
|
+
// This dependency is NOT tracked - changes won't trigger re-run
|
|
37
|
+
if (includeDetails.value) {
|
|
38
|
+
userData.value = { ...data, details: await fetchDetails(data.id) }
|
|
39
|
+
} else {
|
|
40
|
+
userData.value = data
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// BAD: Multiple dependencies after await
|
|
45
|
+
watchEffect(async () => {
|
|
46
|
+
await someAsyncSetup()
|
|
47
|
+
|
|
48
|
+
// None of these are tracked!
|
|
49
|
+
console.log(optionA.value) // Not tracked
|
|
50
|
+
console.log(optionB.value) // Not tracked
|
|
51
|
+
doSomething(optionC.value) // Not tracked
|
|
52
|
+
})
|
|
53
|
+
</script>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Correct:**
|
|
57
|
+
```vue
|
|
58
|
+
<script setup>
|
|
59
|
+
import { ref, watchEffect, watch } from 'vue'
|
|
60
|
+
|
|
61
|
+
const userId = ref(1)
|
|
62
|
+
const includeDetails = ref(true)
|
|
63
|
+
const userData = ref(null)
|
|
64
|
+
|
|
65
|
+
// CORRECT: Access all dependencies before await
|
|
66
|
+
watchEffect(async () => {
|
|
67
|
+
// Capture reactive values synchronously
|
|
68
|
+
const id = userId.value
|
|
69
|
+
const withDetails = includeDetails.value
|
|
70
|
+
|
|
71
|
+
// Now these are tracked
|
|
72
|
+
const response = await fetch(`/api/users/${id}`)
|
|
73
|
+
const data = await response.json()
|
|
74
|
+
|
|
75
|
+
if (withDetails) {
|
|
76
|
+
userData.value = { ...data, details: await fetchDetails(data.id) }
|
|
77
|
+
} else {
|
|
78
|
+
userData.value = data
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// ALTERNATIVE: Use watch with explicit dependencies
|
|
83
|
+
watch(
|
|
84
|
+
[userId, includeDetails],
|
|
85
|
+
async ([id, withDetails]) => {
|
|
86
|
+
const response = await fetch(`/api/users/${id}`)
|
|
87
|
+
const data = await response.json()
|
|
88
|
+
|
|
89
|
+
if (withDetails) {
|
|
90
|
+
userData.value = { ...data, details: await fetchDetails(data.id) }
|
|
91
|
+
} else {
|
|
92
|
+
userData.value = data
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{ immediate: true }
|
|
96
|
+
)
|
|
97
|
+
</script>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Pattern: Extract Dependencies First
|
|
101
|
+
|
|
102
|
+
```vue
|
|
103
|
+
<script setup>
|
|
104
|
+
import { ref, watchEffect } from 'vue'
|
|
105
|
+
|
|
106
|
+
const filters = ref({ status: 'active', sortBy: 'name' })
|
|
107
|
+
const page = ref(1)
|
|
108
|
+
const results = ref([])
|
|
109
|
+
|
|
110
|
+
// CORRECT: Extract all needed values synchronously
|
|
111
|
+
watchEffect(async () => {
|
|
112
|
+
// All dependencies accessed before await - all tracked!
|
|
113
|
+
const { status, sortBy } = filters.value
|
|
114
|
+
const currentPage = page.value
|
|
115
|
+
|
|
116
|
+
// Now safe to do async work
|
|
117
|
+
const response = await fetch(
|
|
118
|
+
`/api/items?status=${status}&sort=${sortBy}&page=${currentPage}`
|
|
119
|
+
)
|
|
120
|
+
results.value = await response.json()
|
|
121
|
+
})
|
|
122
|
+
</script>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## When to Use watch Instead
|
|
126
|
+
|
|
127
|
+
```vue
|
|
128
|
+
<script setup>
|
|
129
|
+
import { ref, watch } from 'vue'
|
|
130
|
+
|
|
131
|
+
const source = ref('initial')
|
|
132
|
+
const option = ref('default')
|
|
133
|
+
const result = ref(null)
|
|
134
|
+
|
|
135
|
+
// BEST for complex async: Use watch with explicit sources
|
|
136
|
+
// All dependencies are explicitly declared and always tracked
|
|
137
|
+
watch(
|
|
138
|
+
[source, option],
|
|
139
|
+
async ([sourceVal, optionVal]) => {
|
|
140
|
+
const data = await processAsync(sourceVal)
|
|
141
|
+
result.value = applyOption(data, optionVal)
|
|
142
|
+
},
|
|
143
|
+
{ immediate: true }
|
|
144
|
+
)
|
|
145
|
+
</script>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Debugging Untracked Dependencies
|
|
149
|
+
|
|
150
|
+
```vue
|
|
151
|
+
<script setup>
|
|
152
|
+
import { ref, watchEffect } from 'vue'
|
|
153
|
+
|
|
154
|
+
const a = ref(1)
|
|
155
|
+
const b = ref(2)
|
|
156
|
+
|
|
157
|
+
watchEffect(async () => {
|
|
158
|
+
console.log('Tracked dependency a:', a.value) // Tracked
|
|
159
|
+
|
|
160
|
+
await someAsyncOperation()
|
|
161
|
+
|
|
162
|
+
console.log('Untracked dependency b:', b.value) // NOT tracked!
|
|
163
|
+
// Changing b.value won't re-run this watchEffect
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
// Test: Change a.value -> watchEffect re-runs
|
|
167
|
+
// Test: Change b.value -> watchEffect does NOT re-run
|
|
168
|
+
</script>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Reference
|
|
172
|
+
- [Vue.js Watchers - watchEffect](https://vuejs.org/guide/essentials/watchers.html#watcheffect)
|
|
173
|
+
- [Vue.js API - watchEffect](https://vuejs.org/api/reactivity-core.html#watcheffect)
|