@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.
Files changed (208) hide show
  1. package/README.md +63 -0
  2. package/index.mjs +139 -0
  3. package/package.json +34 -0
  4. package/skills/create-adaptable-composable/SKILL.md +76 -0
  5. package/skills/vue-best-practices/SKILL.md +154 -0
  6. package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
  7. package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
  8. package/skills/vue-best-practices/references/component-async.md +97 -0
  9. package/skills/vue-best-practices/references/component-data-flow.md +350 -0
  10. package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
  11. package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
  12. package/skills/vue-best-practices/references/component-slots.md +216 -0
  13. package/skills/vue-best-practices/references/component-suspense.md +228 -0
  14. package/skills/vue-best-practices/references/component-teleport.md +108 -0
  15. package/skills/vue-best-practices/references/component-transition-group.md +128 -0
  16. package/skills/vue-best-practices/references/component-transition.md +125 -0
  17. package/skills/vue-best-practices/references/composables.md +290 -0
  18. package/skills/vue-best-practices/references/directives.md +162 -0
  19. package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
  20. package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
  21. package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
  22. package/skills/vue-best-practices/references/plugins.md +166 -0
  23. package/skills/vue-best-practices/references/reactivity.md +346 -0
  24. package/skills/vue-best-practices/references/render-functions.md +201 -0
  25. package/skills/vue-best-practices/references/sfc.md +310 -0
  26. package/skills/vue-best-practices/references/state-management.md +135 -0
  27. package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
  28. package/skills/vue-debug-guides/SKILL.md +205 -0
  29. package/skills/vue-debug-guides/reference/animation-key-for-rerender.md +160 -0
  30. package/skills/vue-debug-guides/reference/animation-transitiongroup-performance.md +241 -0
  31. package/skills/vue-debug-guides/reference/async-component-error-handling.md +115 -0
  32. package/skills/vue-debug-guides/reference/async-component-keepalive-ref-issue.md +112 -0
  33. package/skills/vue-debug-guides/reference/async-component-suspense-control.md +84 -0
  34. package/skills/vue-debug-guides/reference/async-component-vue-router.md +109 -0
  35. package/skills/vue-debug-guides/reference/attrs-event-listener-merging.md +205 -0
  36. package/skills/vue-debug-guides/reference/checkbox-true-false-value-form-submission.md +118 -0
  37. package/skills/vue-debug-guides/reference/cleanup-side-effects.md +172 -0
  38. package/skills/vue-debug-guides/reference/click-events-on-components.md +180 -0
  39. package/skills/vue-debug-guides/reference/component-naming-conflicts.md +159 -0
  40. package/skills/vue-debug-guides/reference/component-ref-requires-defineexpose.md +176 -0
  41. package/skills/vue-debug-guides/reference/composable-avoid-hidden-side-effects.md +208 -0
  42. package/skills/vue-debug-guides/reference/composable-call-location-restrictions.md +141 -0
  43. package/skills/vue-debug-guides/reference/composable-naming-return-pattern.md +139 -0
  44. package/skills/vue-debug-guides/reference/composable-tovalue-inside-watcheffect.md +182 -0
  45. package/skills/vue-debug-guides/reference/composition-api-not-functional-programming.md +120 -0
  46. package/skills/vue-debug-guides/reference/composition-api-script-setup-async-context.md +203 -0
  47. package/skills/vue-debug-guides/reference/composition-api-vs-react-hooks-differences.md +156 -0
  48. package/skills/vue-debug-guides/reference/computed-array-mutation.md +148 -0
  49. package/skills/vue-debug-guides/reference/computed-conditional-dependencies.md +147 -0
  50. package/skills/vue-debug-guides/reference/computed-no-parameters.md +159 -0
  51. package/skills/vue-debug-guides/reference/computed-no-side-effects.md +107 -0
  52. package/skills/vue-debug-guides/reference/computed-return-value-readonly.md +160 -0
  53. package/skills/vue-debug-guides/reference/configure-app-before-mount.md +89 -0
  54. package/skills/vue-debug-guides/reference/declare-emits-for-documentation.md +212 -0
  55. package/skills/vue-debug-guides/reference/define-expose-before-await.md +192 -0
  56. package/skills/vue-debug-guides/reference/define-model-default-value-sync.md +139 -0
  57. package/skills/vue-debug-guides/reference/defineEmits-must-be-top-level.md +164 -0
  58. package/skills/vue-debug-guides/reference/defineEmits-no-runtime-and-type-mixed.md +170 -0
  59. package/skills/vue-debug-guides/reference/definemodel-object-mutation-no-emit.md +148 -0
  60. package/skills/vue-debug-guides/reference/dom-update-timing-nexttick.md +90 -0
  61. package/skills/vue-debug-guides/reference/dynamic-argument-constraints.md +146 -0
  62. package/skills/vue-debug-guides/reference/dynamic-component-registration-vite.md +147 -0
  63. package/skills/vue-debug-guides/reference/event-modifier-order-matters.md +101 -0
  64. package/skills/vue-debug-guides/reference/exact-modifier-for-precise-shortcuts.md +155 -0
  65. package/skills/vue-debug-guides/reference/fallthrough-attrs-overwrite-vue3.md +159 -0
  66. package/skills/vue-debug-guides/reference/in-dom-template-parsing-caveats.md +149 -0
  67. package/skills/vue-debug-guides/reference/inheritattrs-false-for-wrapper-components.md +230 -0
  68. package/skills/vue-debug-guides/reference/keepalive-router-nested-double-mount.md +222 -0
  69. package/skills/vue-debug-guides/reference/keepalive-transition-memory-leak.md +144 -0
  70. package/skills/vue-debug-guides/reference/keyup-modifier-timing.md +137 -0
  71. package/skills/vue-debug-guides/reference/lifecycle-dom-access-timing.md +216 -0
  72. package/skills/vue-debug-guides/reference/lifecycle-hooks-synchronous-registration.md +156 -0
  73. package/skills/vue-debug-guides/reference/lifecycle-ssr-awareness.md +184 -0
  74. package/skills/vue-debug-guides/reference/local-components-not-in-descendants.md +151 -0
  75. package/skills/vue-debug-guides/reference/mount-return-value.md +88 -0
  76. package/skills/vue-debug-guides/reference/multi-root-component-class-attrs.md +93 -0
  77. package/skills/vue-debug-guides/reference/native-event-collision-with-emits.md +162 -0
  78. package/skills/vue-debug-guides/reference/no-passive-with-prevent.md +141 -0
  79. package/skills/vue-debug-guides/reference/no-v-if-with-v-for.md +136 -0
  80. package/skills/vue-debug-guides/reference/perf-computed-object-stability.md +157 -0
  81. package/skills/vue-debug-guides/reference/perf-props-stability-update-optimization.md +140 -0
  82. package/skills/vue-debug-guides/reference/plugin-global-properties-sparingly.md +109 -0
  83. package/skills/vue-debug-guides/reference/plugin-install-before-mount.md +124 -0
  84. package/skills/vue-debug-guides/reference/plugin-prefer-provide-inject-over-global-properties.md +120 -0
  85. package/skills/vue-debug-guides/reference/plugin-typescript-type-augmentation.md +157 -0
  86. package/skills/vue-debug-guides/reference/prop-defineprops-scope-limitation.md +161 -0
  87. package/skills/vue-debug-guides/reference/provide-inject-debugging-challenges.md +203 -0
  88. package/skills/vue-debug-guides/reference/provide-inject-default-value-factory.md +244 -0
  89. package/skills/vue-debug-guides/reference/provide-inject-reactivity-not-automatic.md +226 -0
  90. package/skills/vue-debug-guides/reference/provide-inject-synchronous-setup.md +235 -0
  91. package/skills/vue-debug-guides/reference/reactive-destructuring.md +89 -0
  92. package/skills/vue-debug-guides/reference/reactivity-debugging-hooks.md +132 -0
  93. package/skills/vue-debug-guides/reference/reactivity-markraw-for-non-reactive.md +149 -0
  94. package/skills/vue-debug-guides/reference/reactivity-proxy-identity-hazard.md +96 -0
  95. package/skills/vue-debug-guides/reference/reactivity-same-tick-batching.md +166 -0
  96. package/skills/vue-debug-guides/reference/ref-value-access.md +61 -0
  97. package/skills/vue-debug-guides/reference/refs-in-collections-need-value.md +81 -0
  98. package/skills/vue-debug-guides/reference/render-function-avoid-internal-vnode-properties.md +151 -0
  99. package/skills/vue-debug-guides/reference/render-function-vnodes-must-be-unique.md +133 -0
  100. package/skills/vue-debug-guides/reference/rendering-render-function-h-import-vue3.md +148 -0
  101. package/skills/vue-debug-guides/reference/rendering-render-function-return-from-setup.md +148 -0
  102. package/skills/vue-debug-guides/reference/rendering-render-function-slots-as-functions.md +168 -0
  103. package/skills/vue-debug-guides/reference/rendering-resolve-component-for-string-names.md +231 -0
  104. package/skills/vue-debug-guides/reference/select-initial-value-ios-bug.md +91 -0
  105. package/skills/vue-debug-guides/reference/self-referencing-component-name.md +157 -0
  106. package/skills/vue-debug-guides/reference/sfc-named-exports-forbidden.md +184 -0
  107. package/skills/vue-debug-guides/reference/sfc-scoped-css-child-component-styling.md +156 -0
  108. package/skills/vue-debug-guides/reference/sfc-scoped-css-dynamic-content.md +193 -0
  109. package/skills/vue-debug-guides/reference/sfc-scoped-css-slot-content.md +242 -0
  110. package/skills/vue-debug-guides/reference/sfc-script-setup-reactivity.md +195 -0
  111. package/skills/vue-debug-guides/reference/slot-forwarding-to-child-components.md +143 -0
  112. package/skills/vue-debug-guides/reference/slot-implicit-default-content.md +155 -0
  113. package/skills/vue-debug-guides/reference/slot-name-reserved-prop.md +109 -0
  114. package/skills/vue-debug-guides/reference/slot-named-scoped-explicit-default.md +95 -0
  115. package/skills/vue-debug-guides/reference/slot-render-scope-parent-only.md +135 -0
  116. package/skills/vue-debug-guides/reference/slot-v-slot-on-components-or-templates-only.md +122 -0
  117. package/skills/vue-debug-guides/reference/ssr-hydration-mismatch-causes.md +280 -0
  118. package/skills/vue-debug-guides/reference/ssr-platform-specific-apis.md +256 -0
  119. package/skills/vue-debug-guides/reference/state-ssr-cross-request-pollution.md +276 -0
  120. package/skills/vue-debug-guides/reference/suspense-no-builtin-error-handling.md +127 -0
  121. package/skills/vue-debug-guides/reference/suspense-ssr-hydration-issues.md +159 -0
  122. package/skills/vue-debug-guides/reference/tailwind-dynamic-class-generation.md +144 -0
  123. package/skills/vue-debug-guides/reference/teleport-scoped-styles-limitation.md +191 -0
  124. package/skills/vue-debug-guides/reference/teleport-ssr-hydration.md +152 -0
  125. package/skills/vue-debug-guides/reference/teleport-target-must-exist.md +113 -0
  126. package/skills/vue-debug-guides/reference/template-expressions-restrictions.md +114 -0
  127. package/skills/vue-debug-guides/reference/template-functions-no-side-effects.md +187 -0
  128. package/skills/vue-debug-guides/reference/template-ref-null-with-v-if.md +123 -0
  129. package/skills/vue-debug-guides/reference/template-ref-unwrapping-top-level.md +104 -0
  130. package/skills/vue-debug-guides/reference/template-ref-v-for-order.md +172 -0
  131. package/skills/vue-debug-guides/reference/textarea-no-interpolation.md +72 -0
  132. package/skills/vue-debug-guides/reference/transition-group-flip-inline-elements.md +152 -0
  133. package/skills/vue-debug-guides/reference/transition-group-move-animation-position-absolute.md +130 -0
  134. package/skills/vue-debug-guides/reference/transition-group-no-default-wrapper-vue3.md +152 -0
  135. package/skills/vue-debug-guides/reference/transition-js-hooks-done-callback.md +251 -0
  136. package/skills/vue-debug-guides/reference/transition-nested-duration.md +182 -0
  137. package/skills/vue-debug-guides/reference/transition-reusable-scoped-style.md +245 -0
  138. package/skills/vue-debug-guides/reference/transition-router-view-appear.md +193 -0
  139. package/skills/vue-debug-guides/reference/transition-type-when-mixed.md +172 -0
  140. package/skills/vue-debug-guides/reference/transition-unmount-hook-timing.md +149 -0
  141. package/skills/vue-debug-guides/reference/ts-defineprops-boolean-default-false.md +225 -0
  142. package/skills/vue-debug-guides/reference/ts-defineprops-imported-types-limitations.md +281 -0
  143. package/skills/vue-debug-guides/reference/ts-event-handler-explicit-typing.md +213 -0
  144. package/skills/vue-debug-guides/reference/ts-reactive-no-generic-argument.md +196 -0
  145. package/skills/vue-debug-guides/reference/ts-shallowref-for-dynamic-components.md +218 -0
  146. package/skills/vue-debug-guides/reference/ts-template-ref-null-handling.md +249 -0
  147. package/skills/vue-debug-guides/reference/ts-template-type-casting.md +214 -0
  148. package/skills/vue-debug-guides/reference/ts-withdefaults-mutable-factory-function.md +171 -0
  149. package/skills/vue-debug-guides/reference/undeclared-emits-double-firing.md +195 -0
  150. package/skills/vue-debug-guides/reference/use-template-ref-vue35.md +158 -0
  151. package/skills/vue-debug-guides/reference/v-else-must-follow-v-if.md +136 -0
  152. package/skills/vue-debug-guides/reference/v-for-component-props.md +95 -0
  153. package/skills/vue-debug-guides/reference/v-for-computed-reverse-sort.md +86 -0
  154. package/skills/vue-debug-guides/reference/v-for-key-attribute.md +90 -0
  155. package/skills/vue-debug-guides/reference/v-for-range-starts-at-one.md +66 -0
  156. package/skills/vue-debug-guides/reference/v-if-null-check-order.md +171 -0
  157. package/skills/vue-debug-guides/reference/v-model-ignores-html-attributes.md +83 -0
  158. package/skills/vue-debug-guides/reference/v-model-ime-composition.md +83 -0
  159. package/skills/vue-debug-guides/reference/v-model-number-modifier-behavior.md +124 -0
  160. package/skills/vue-debug-guides/reference/v-show-template-limitation.md +124 -0
  161. package/skills/vue-debug-guides/reference/watch-async-cleanup.md +180 -0
  162. package/skills/vue-debug-guides/reference/watch-async-creation-memory-leak.md +176 -0
  163. package/skills/vue-debug-guides/reference/watch-deep-same-object-reference.md +165 -0
  164. package/skills/vue-debug-guides/reference/watch-flush-timing.md +189 -0
  165. package/skills/vue-debug-guides/reference/watch-reactive-property-getter.md +108 -0
  166. package/skills/vue-debug-guides/reference/watcheffect-async-dependency-tracking.md +173 -0
  167. package/skills/vue-debug-guides/reference/watcheffect-flush-post-for-refs.md +176 -0
  168. package/skills/vue-jsx-best-practices/SKILL.md +12 -0
  169. package/skills/vue-jsx-best-practices/reference/render-function-jsx-vue-vs-react.md +141 -0
  170. package/skills/vue-options-api-best-practices/SKILL.md +23 -0
  171. package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-lifecycle-hooks.md +95 -0
  172. package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-methods.md +68 -0
  173. package/skills/vue-options-api-best-practices/reference/stateful-methods-lifecycle.md +61 -0
  174. package/skills/vue-options-api-best-practices/reference/ts-options-api-arrow-functions-validators.md +141 -0
  175. package/skills/vue-options-api-best-practices/reference/ts-options-api-computed-return-types.md +192 -0
  176. package/skills/vue-options-api-best-practices/reference/ts-options-api-proptype-complex-types.md +212 -0
  177. package/skills/vue-options-api-best-practices/reference/ts-options-api-provide-inject-limitations.md +135 -0
  178. package/skills/vue-options-api-best-practices/reference/ts-options-api-type-event-handlers.md +202 -0
  179. package/skills/vue-options-api-best-practices/reference/ts-options-api-use-definecomponent.md +172 -0
  180. package/skills/vue-options-api-best-practices/reference/ts-strict-mode-options-api.md +197 -0
  181. package/skills/vue-pinia-best-practices/SKILL.md +21 -0
  182. package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
  183. package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
  184. package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
  185. package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
  186. package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
  187. package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
  188. package/skills/vue-router-best-practices/SKILL.md +23 -0
  189. package/skills/vue-router-best-practices/reference/router-beforeenter-no-param-trigger.md +167 -0
  190. package/skills/vue-router-best-practices/reference/router-beforerouteenter-no-this.md +176 -0
  191. package/skills/vue-router-best-practices/reference/router-guard-async-await-pattern.md +227 -0
  192. package/skills/vue-router-best-practices/reference/router-navigation-guard-infinite-loop.md +187 -0
  193. package/skills/vue-router-best-practices/reference/router-navigation-guard-next-deprecated.md +150 -0
  194. package/skills/vue-router-best-practices/reference/router-param-change-no-lifecycle.md +181 -0
  195. package/skills/vue-router-best-practices/reference/router-simple-routing-cleanup.md +209 -0
  196. package/skills/vue-router-best-practices/reference/router-use-vue-router-for-production.md +183 -0
  197. package/skills/vue-testing-best-practices/SKILL.md +29 -0
  198. package/skills/vue-testing-best-practices/reference/async-component-testing.md +163 -0
  199. package/skills/vue-testing-best-practices/reference/teleport-testing-complexity.md +158 -0
  200. package/skills/vue-testing-best-practices/reference/testing-async-await-flushpromises.md +175 -0
  201. package/skills/vue-testing-best-practices/reference/testing-browser-vs-node-runners.md +208 -0
  202. package/skills/vue-testing-best-practices/reference/testing-component-blackbox-approach.md +144 -0
  203. package/skills/vue-testing-best-practices/reference/testing-composables-helper-wrapper.md +238 -0
  204. package/skills/vue-testing-best-practices/reference/testing-e2e-playwright-recommended.md +242 -0
  205. package/skills/vue-testing-best-practices/reference/testing-no-snapshot-only.md +197 -0
  206. package/skills/vue-testing-best-practices/reference/testing-pinia-store-setup.md +228 -0
  207. package/skills/vue-testing-best-practices/reference/testing-suspense-async-components.md +229 -0
  208. 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)