@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,182 @@
1
+ ---
2
+ title: Call toValue() Inside watchEffect for Proper Dependency Tracking
3
+ impact: HIGH
4
+ impactDescription: Calling toValue() outside watchEffect prevents reactive dependency tracking, causing the effect to never re-run
5
+ type: gotcha
6
+ tags: [vue3, composables, composition-api, watchEffect, toValue, reactivity]
7
+ ---
8
+
9
+ # Call toValue() Inside watchEffect for Proper Dependency Tracking
10
+
11
+ **Impact: HIGH** - When writing composables that accept `MaybeRefOrGetter` arguments, you must call `toValue()` inside the `watchEffect` callback, not outside. If you extract the value before the watchEffect, Vue cannot track the dependency and the effect will never re-run when the source changes.
12
+
13
+ This is a subtle but critical mistake that leads to composables that work with initial values but never update.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Always call `toValue()` inside `watchEffect` callbacks, not before
18
+ - [ ] Similarly, access `.value` on refs inside watchEffect, not outside
19
+ - [ ] For `watch()`, use a getter function that calls `toValue()`
20
+ - [ ] Test that composables update when their inputs change
21
+
22
+ **Incorrect:**
23
+ ```javascript
24
+ import { ref, watchEffect, toValue } from 'vue'
25
+
26
+ export function useFetch(url) {
27
+ const data = ref(null)
28
+ const error = ref(null)
29
+
30
+ // WRONG: toValue called outside watchEffect
31
+ // This extracts the value ONCE and passes a static string
32
+ const urlValue = toValue(url)
33
+
34
+ watchEffect(async () => {
35
+ try {
36
+ // urlValue is a static string - no dependency tracked!
37
+ const response = await fetch(urlValue)
38
+ data.value = await response.json()
39
+ } catch (e) {
40
+ error.value = e
41
+ }
42
+ })
43
+
44
+ return { data, error }
45
+ }
46
+
47
+ // When used like this:
48
+ const apiUrl = ref('/api/users')
49
+ const { data } = useFetch(apiUrl)
50
+
51
+ // Later...
52
+ apiUrl.value = '/api/products' // useFetch will NOT refetch!
53
+ ```
54
+
55
+ **Correct:**
56
+ ```javascript
57
+ import { ref, watchEffect, toValue } from 'vue'
58
+
59
+ export function useFetch(url) {
60
+ const data = ref(null)
61
+ const error = ref(null)
62
+
63
+ watchEffect(async () => {
64
+ // CORRECT: toValue called INSIDE watchEffect
65
+ // Vue tracks this as a dependency
66
+ const urlValue = toValue(url)
67
+
68
+ try {
69
+ const response = await fetch(urlValue)
70
+ data.value = await response.json()
71
+ } catch (e) {
72
+ error.value = e
73
+ }
74
+ })
75
+
76
+ return { data, error }
77
+ }
78
+
79
+ // Now when used:
80
+ const apiUrl = ref('/api/users')
81
+ const { data } = useFetch(apiUrl)
82
+
83
+ // Later...
84
+ apiUrl.value = '/api/products' // useFetch WILL refetch!
85
+ ```
86
+
87
+ ## The Same Applies to Direct Ref Access
88
+
89
+ ```javascript
90
+ // WRONG: Accessing .value outside the effect
91
+ export function useDebounce(source, delay = 300) {
92
+ // This captures the initial value, not a reactive dependency
93
+ const initialValue = source.value // or toValue(source)
94
+
95
+ watchEffect(() => {
96
+ // initialValue is static - this only runs once
97
+ console.log('Value:', initialValue)
98
+ })
99
+ }
100
+
101
+ // CORRECT: Access inside the effect
102
+ export function useDebounce(source, delay = 300) {
103
+ watchEffect(() => {
104
+ // Vue tracks source.value or toValue(source) as dependency
105
+ console.log('Value:', toValue(source))
106
+ })
107
+ }
108
+ ```
109
+
110
+ ## Pattern: Using watch() with Getter Functions
111
+
112
+ For `watch()`, wrap `toValue()` in a getter:
113
+
114
+ ```javascript
115
+ import { ref, watch, toValue } from 'vue'
116
+
117
+ export function useLocalStorage(key, defaultValue) {
118
+ const data = ref(defaultValue)
119
+
120
+ // CORRECT: Use getter function with watch
121
+ watch(
122
+ () => toValue(key), // Getter calls toValue, tracks dependency
123
+ (newKey) => {
124
+ const stored = localStorage.getItem(newKey)
125
+ data.value = stored ? JSON.parse(stored) : defaultValue
126
+ },
127
+ { immediate: true }
128
+ )
129
+
130
+ return data
131
+ }
132
+ ```
133
+
134
+ ## Why This Happens
135
+
136
+ Vue's reactivity tracking works by detecting property accesses during effect execution:
137
+
138
+ ```javascript
139
+ watchEffect(() => {
140
+ // When this runs, Vue is "recording" what reactive sources are accessed
141
+ const value = someRef.value // Vue records: "this effect depends on someRef"
142
+ })
143
+
144
+ // But if you extract the value before:
145
+ const value = someRef.value // Vue isn't recording yet
146
+ watchEffect(() => {
147
+ console.log(value) // Just using a plain JavaScript variable
148
+ })
149
+ ```
150
+
151
+ `toValue()` works the same way - it accesses `.value` internally, so it must happen during effect execution for tracking to work.
152
+
153
+ ## Quick Checklist for Composable Authors
154
+
155
+ When accepting `MaybeRefOrGetter` inputs:
156
+
157
+ 1. Store the raw argument (don't call `toValue` during setup)
158
+ 2. Call `toValue()` inside any reactive context (`watchEffect`, `watch`, `computed`)
159
+ 3. Test with both static values AND refs that change
160
+
161
+ ```javascript
162
+ export function useMyComposable(input) {
163
+ // Store raw - don't extract value here
164
+ // const value = toValue(input) // WRONG
165
+
166
+ const result = computed(() => {
167
+ // Extract value inside reactive context
168
+ return transform(toValue(input)) // CORRECT
169
+ })
170
+
171
+ watchEffect(() => {
172
+ // Extract value inside reactive context
173
+ doSomething(toValue(input)) // CORRECT
174
+ })
175
+
176
+ return { result }
177
+ }
178
+ ```
179
+
180
+ ## Reference
181
+ - [Vue.js Reactivity API - toValue](https://vuejs.org/api/reactivity-utilities.html#tovalue)
182
+ - [Vue.js Composables - Accepting Ref Arguments](https://vuejs.org/guide/reusability/composables.html#accepting-reactive-state)
@@ -0,0 +1,120 @@
1
+ ---
2
+ title: Composition API Uses Mutable Reactivity, Not Functional Programming
3
+ impact: MEDIUM
4
+ impactDescription: Misunderstanding the paradigm leads to incorrect state management patterns
5
+ type: gotcha
6
+ tags: [vue3, composition-api, reactivity, functional-programming, paradigm]
7
+ ---
8
+
9
+ # Composition API Uses Mutable Reactivity, Not Functional Programming
10
+
11
+ **Impact: MEDIUM** - Despite being function-based, the Composition API follows Vue's mutable, fine-grained reactivity paradigm—NOT functional programming principles. Treating it like a functional paradigm leads to incorrect patterns like unnecessary cloning, immutable-style updates, or avoiding mutation when mutation is the intended pattern.
12
+
13
+ Vue's Composition API leverages imported functions to organize code, but the underlying model is based on mutable reactive state that Vue tracks and responds to. This is fundamentally different from functional programming with immutability (like Redux reducers).
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Mutate reactive state directly - don't create new objects for every update
18
+ - [ ] Don't apply immutability patterns unnecessarily (spreading, Object.assign for updates)
19
+ - [ ] Understand that `ref()` and `reactive()` enable mutable state tracking
20
+ - [ ] Use Vue's reactivity as intended: direct mutation with automatic tracking
21
+
22
+ **Incorrect:**
23
+ ```javascript
24
+ import { ref } from 'vue'
25
+
26
+ const todos = ref([])
27
+
28
+ // WRONG: Treating Vue like Redux/functional - unnecessary immutability
29
+ function addTodo(todo) {
30
+ // Creating a new array every time is wasteful in Vue
31
+ todos.value = [...todos.value, todo]
32
+ }
33
+
34
+ function updateTodo(id, updates) {
35
+ // Unnecessary spread - Vue tracks mutations directly
36
+ todos.value = todos.value.map(t =>
37
+ t.id === id ? { ...t, ...updates } : t
38
+ )
39
+ }
40
+
41
+ const user = ref({ name: 'John', age: 30 })
42
+
43
+ // WRONG: Creating new object for simple update
44
+ function updateName(newName) {
45
+ user.value = { ...user.value, name: newName }
46
+ }
47
+ ```
48
+
49
+ **Correct:**
50
+ ```javascript
51
+ import { ref, reactive } from 'vue'
52
+
53
+ const todos = ref([])
54
+
55
+ // CORRECT: Mutate directly - Vue tracks the change
56
+ function addTodo(todo) {
57
+ todos.value.push(todo) // Direct mutation is the Vue way
58
+ }
59
+
60
+ function updateTodo(id, updates) {
61
+ const todo = todos.value.find(t => t.id === id)
62
+ if (todo) {
63
+ Object.assign(todo, updates) // Direct mutation
64
+ }
65
+ }
66
+
67
+ const user = ref({ name: 'John', age: 30 })
68
+
69
+ // CORRECT: Mutate the property directly
70
+ function updateName(newName) {
71
+ user.value.name = newName // Vue tracks this!
72
+ }
73
+
74
+ // Or with reactive():
75
+ const state = reactive({ name: 'John', age: 30 })
76
+
77
+ function updateNameReactive(newName) {
78
+ state.name = newName // Direct mutation, reactivity preserved
79
+ }
80
+ ```
81
+
82
+ ## When Immutability Patterns Make Sense
83
+
84
+ ```javascript
85
+ // Immutability IS appropriate when:
86
+
87
+ // 1. Replacing the entire state (e.g., from API response)
88
+ const users = ref([])
89
+ async function fetchUsers() {
90
+ users.value = await api.getUsers() // Complete replacement is fine
91
+ }
92
+
93
+ // 2. When you need a snapshot for comparison
94
+ const previousState = { ...currentState } // For undo/redo
95
+
96
+ // 3. When passing data to external libraries expecting immutable data
97
+ const chartData = computed(() => [...rawData.value]) // Copy for chart lib
98
+ ```
99
+
100
+ ## The Vue Mental Model
101
+
102
+ ```javascript
103
+ // Vue's reactivity is like a spreadsheet:
104
+ // - Cell A1 contains a value (ref)
105
+ // - Cell B1 has a formula referencing A1 (computed)
106
+ // - Change A1, and B1 automatically updates
107
+
108
+ const a1 = ref(10)
109
+ const b1 = computed(() => a1.value * 2)
110
+
111
+ // You CHANGE A1 (mutate), you don't create a new A1
112
+ a1.value = 20 // b1 automatically becomes 40
113
+
114
+ // This is fundamentally different from:
115
+ // state = reducer(state, action) // Functional/Redux pattern
116
+ ```
117
+
118
+ ## Reference
119
+ - [Composition API FAQ](https://vuejs.org/guide/extras/composition-api-faq.html)
120
+ - [Reactivity Fundamentals](https://vuejs.org/guide/essentials/reactivity-fundamentals.html)
@@ -0,0 +1,203 @@
1
+ ---
2
+ title: Top-Level await in script setup Preserves Component Context
3
+ impact: HIGH
4
+ impactDescription: Misunderstanding async context causes lifecycle hooks and watchers to silently fail
5
+ type: gotcha
6
+ tags: [vue3, composition-api, script-setup, async, await, suspense]
7
+ ---
8
+
9
+ # Top-Level await in script setup Preserves Component Context
10
+
11
+ **Impact: HIGH** - In `<script setup>`, top-level `await` statements preserve component context (allowing lifecycle hooks and watchers after `await`), but this is a special case. Nested async functions or callbacks lose context, causing lifecycle hooks to silently fail.
12
+
13
+ Vue's compiler automatically injects context restoration after each top-level await in `<script setup>`. This doesn't apply to `setup()` function or nested async contexts.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Understand that top-level await in `<script setup>` is specially handled
18
+ - [ ] Never register lifecycle hooks in nested async functions
19
+ - [ ] Use `<Suspense>` when using async `<script setup>` components
20
+ - [ ] In regular `setup()`, never use await before lifecycle hook registration
21
+ - [ ] Register hooks synchronously, then do async work inside them
22
+
23
+ **Top-Level await Works (script setup only):**
24
+ ```vue
25
+ <script setup>
26
+ import { ref, onMounted, watch } from 'vue'
27
+
28
+ // This is TOP-LEVEL await - Vue compiler preserves context
29
+ const config = await fetchConfig() // OK!
30
+
31
+ // These hooks work because Vue restored context
32
+ onMounted(() => {
33
+ console.log('This will run!') // Works
34
+ })
35
+
36
+ watch(someRef, () => {
37
+ console.log('This will track!') // Works
38
+ })
39
+
40
+ // Another top-level await - still OK
41
+ const data = await fetchData(config.apiUrl) // OK!
42
+
43
+ // Still works after multiple awaits
44
+ onMounted(() => {
45
+ console.log('This also runs!') // Works
46
+ })
47
+ </script>
48
+
49
+ <!-- IMPORTANT: Parent must use Suspense -->
50
+ <template>
51
+ <Suspense>
52
+ <AsyncComponent />
53
+ </Suspense>
54
+ </template>
55
+ ```
56
+
57
+ **Nested Async Breaks Context:**
58
+ ```vue
59
+ <script setup>
60
+ import { ref, onMounted, watch } from 'vue'
61
+
62
+ // WRONG: Nested async function - context lost after await
63
+ async function initializeData() {
64
+ const config = await fetchConfig()
65
+
66
+ // BUG: This hook will NOT be registered!
67
+ // We're no longer in the synchronous setup context
68
+ onMounted(() => {
69
+ console.log('This will NEVER run!') // Silent failure
70
+ })
71
+
72
+ // BUG: This watcher won't auto-dispose on unmount
73
+ watch(someRef, () => {
74
+ console.log('Memory leak - not cleaned up!')
75
+ })
76
+ }
77
+
78
+ // Calling the async function
79
+ initializeData() // Hooks inside won't work!
80
+
81
+ // WRONG: Callbacks also lose context
82
+ setTimeout(async () => {
83
+ await delay(100)
84
+ onMounted(() => {
85
+ console.log('Never runs!') // Silent failure
86
+ })
87
+ }, 0)
88
+ </script>
89
+ ```
90
+
91
+ **Correct Patterns:**
92
+ ```vue
93
+ <script setup>
94
+ import { ref, onMounted, watch } from 'vue'
95
+
96
+ const data = ref(null)
97
+ const config = ref(null)
98
+
99
+ // CORRECT: Register hooks synchronously FIRST
100
+ onMounted(async () => {
101
+ // Then do async work INSIDE the hook
102
+ config.value = await fetchConfig()
103
+ data.value = await fetchData(config.value.apiUrl)
104
+ })
105
+
106
+ // CORRECT: Watchers registered synchronously
107
+ watch(config, async (newConfig) => {
108
+ if (newConfig) {
109
+ data.value = await fetchData(newConfig.apiUrl)
110
+ }
111
+ })
112
+
113
+ // Or use top-level await for initial data
114
+ const initialConfig = await fetchConfig() // OK - top level
115
+ config.value = initialConfig
116
+
117
+ onMounted(() => {
118
+ console.log('Works!') // Context preserved by compiler
119
+ })
120
+ </script>
121
+ ```
122
+
123
+ **setup() Function (Not script setup):**
124
+ ```javascript
125
+ // In regular setup(), await ALWAYS breaks context
126
+ export default {
127
+ async setup() {
128
+ const data = ref(null)
129
+
130
+ // WRONG: Hooks after await won't register
131
+ const config = await fetchConfig()
132
+
133
+ onMounted(() => {
134
+ console.log('Never runs!') // Silent failure!
135
+ })
136
+
137
+ return { data }
138
+ }
139
+ }
140
+
141
+ // CORRECT: Register hooks before any await
142
+ export default {
143
+ async setup() {
144
+ const data = ref(null)
145
+
146
+ // Register hooks FIRST (synchronous)
147
+ onMounted(async () => {
148
+ const config = await fetchConfig()
149
+ data.value = await fetchData(config)
150
+ })
151
+
152
+ // Now you can await if needed
153
+ // But hooks must be registered before this point
154
+
155
+ return { data }
156
+ }
157
+ }
158
+ ```
159
+
160
+ ## Why This Happens
161
+
162
+ ```javascript
163
+ // Vue tracks the "current component instance" during setup
164
+ // This is like a global variable that gets set and cleared
165
+
166
+ // During synchronous setup:
167
+ function setup() {
168
+ currentInstance = this // Vue sets this
169
+
170
+ onMounted(cb) // Uses currentInstance to register
171
+
172
+ // After await, JavaScript resumes in a microtask
173
+ await something()
174
+
175
+ // currentInstance is now null or different!
176
+ onMounted(cb) // Can't find the instance - silently fails
177
+ }
178
+
179
+ // <script setup> compiler adds restoration:
180
+ // After each await, it injects: setCurrentInstance(savedInstance)
181
+ ```
182
+
183
+ ## Suspense Requirement
184
+
185
+ ```vue
186
+ <!-- When using async script setup, parent needs Suspense -->
187
+ <template>
188
+ <Suspense>
189
+ <!-- Async component with top-level await -->
190
+ <AsyncChild />
191
+
192
+ <!-- Optional: Loading state -->
193
+ <template #fallback>
194
+ <LoadingSpinner />
195
+ </template>
196
+ </Suspense>
197
+ </template>
198
+ ```
199
+
200
+ ## Reference
201
+ - [Composition API FAQ - Async Setup](https://vuejs.org/guide/extras/composition-api-faq.html)
202
+ - [Composables - Async Without Await](https://antfu.me/posts/async-with-composition-api)
203
+ - [Suspense](https://vuejs.org/guide/built-ins/suspense.html)
@@ -0,0 +1,156 @@
1
+ ---
2
+ title: Vue Composition API Runs Once, Unlike React Hooks
3
+ impact: MEDIUM
4
+ impactDescription: Understanding this difference prevents over-engineering and React patterns that don't apply
5
+ type: gotcha
6
+ tags: [vue3, composition-api, react-hooks, setup, stale-closure]
7
+ ---
8
+
9
+ # Vue Composition API Runs Once, Unlike React Hooks
10
+
11
+ **Impact: MEDIUM** - Vue's `setup()` or `<script setup>` executes only once per component instance, while React Hooks run on every render. Developers coming from React often apply patterns (dependency arrays, excessive memoization, useCallback) that are unnecessary and counterproductive in Vue.
12
+
13
+ Understanding this fundamental difference is crucial for writing idiomatic Vue code. Vue's approach eliminates entire categories of bugs (stale closures, exhaustive deps) that plague React applications.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Don't implement "dependency arrays" - Vue tracks dependencies automatically
18
+ - [ ] Don't wrap functions in "useCallback" equivalents - not needed in Vue
19
+ - [ ] Don't use "useMemo" patterns - Vue's `computed()` handles this automatically
20
+ - [ ] Understand that closures in Vue don't go "stale" like in React
21
+ - [ ] Don't worry about "call order" - Vue composables can be conditional
22
+
23
+ **React Patterns to Avoid in Vue:**
24
+ ```javascript
25
+ // These patterns are UNNECESSARY in Vue - they solve React-specific problems
26
+
27
+ // WRONG: Trying to implement dependency arrays (React pattern)
28
+ watch(
29
+ [dep1, dep2, dep3], // Vue tracks deps automatically in watchEffect
30
+ () => {
31
+ // ...
32
+ }
33
+ )
34
+ // Unless you specifically WANT to control which deps trigger the watcher,
35
+ // prefer watchEffect() which auto-tracks
36
+
37
+ // WRONG: Memoizing callbacks like useCallback
38
+ const memoizedHandler = computed(() => {
39
+ return () => doSomething(state.value)
40
+ })
41
+ // In Vue, just define the function normally - no memoization needed
42
+
43
+ // WRONG: Worrying about stale closures
44
+ function useData() {
45
+ const data = ref(null)
46
+
47
+ // In React, this could capture stale 'data' - NOT in Vue!
48
+ // Vue refs are always current
49
+ const handler = () => {
50
+ console.log(data.value) // Always gets current value
51
+ }
52
+
53
+ return { data, handler }
54
+ }
55
+ ```
56
+
57
+ **Correct Vue Patterns:**
58
+ ```javascript
59
+ import { ref, computed, watchEffect } from 'vue'
60
+
61
+ // CORRECT: Auto-dependency tracking with watchEffect
62
+ const query = ref('')
63
+ const filter = ref('all')
64
+
65
+ watchEffect(() => {
66
+ // Vue automatically detects that this depends on query and filter
67
+ // No dependency array needed!
68
+ fetchResults(query.value, filter.value)
69
+ })
70
+
71
+ // CORRECT: computed() handles memoization automatically
72
+ const expensiveResult = computed(() => {
73
+ // Only recalculates when dependencies actually change
74
+ return heavyComputation(data.value)
75
+ })
76
+
77
+ // CORRECT: Functions don't need memoization
78
+ function handleClick() {
79
+ count.value++
80
+ }
81
+ // Just use it directly - no useCallback wrapper needed
82
+ // <button @click="handleClick">
83
+
84
+ // CORRECT: Closures always access current values
85
+ const count = ref(0)
86
+ const message = ref('')
87
+
88
+ function logState() {
89
+ // This always logs CURRENT values, never stale ones
90
+ console.log(`Count: ${count.value}, Message: ${message.value}`)
91
+ }
92
+
93
+ setTimeout(() => {
94
+ logState() // Gets current values even if called later
95
+ }, 5000)
96
+ ```
97
+
98
+ ## Vue's Advantages Over React Hooks
99
+
100
+ ```javascript
101
+ // 1. No stale closure problems
102
+ const count = ref(0)
103
+
104
+ onMounted(() => {
105
+ setInterval(() => {
106
+ // In React: would need useRef or deps array to avoid stale value
107
+ // In Vue: count.value is always current
108
+ console.log(count.value)
109
+ }, 1000)
110
+ })
111
+
112
+ // 2. Composables can be conditional
113
+ if (featureEnabled) {
114
+ const { data } = useSomeFeature() // This is FINE in Vue!
115
+ }
116
+ // In React: "Hooks cannot be conditional" - not a problem in Vue
117
+
118
+ // 3. No exhaustive-deps linting headaches
119
+ watchEffect(() => {
120
+ // Use any reactive values - Vue tracks them all automatically
121
+ // No ESLint rule yelling about missing dependencies
122
+ doSomething(a.value, b.value, c.value)
123
+ })
124
+
125
+ // 4. Child components don't need memoization by default
126
+ // Vue's reactivity system only updates what actually changed
127
+ // No need for React.memo() equivalents in most cases
128
+ ```
129
+
130
+ ## When Vue Patterns Differ
131
+
132
+ ```javascript
133
+ // Setup runs once - so initialization happens once
134
+ <script setup>
135
+ import { ref, onMounted } from 'vue'
136
+
137
+ // This code runs ONCE when component is created
138
+ const data = ref(null)
139
+ console.log('Setup running') // Only logs once
140
+
141
+ onMounted(() => {
142
+ console.log('Mounted') // Only logs once
143
+ })
144
+
145
+ // If you need something to run on every reactive change,
146
+ // use watch or watchEffect
147
+ watchEffect(() => {
148
+ // This runs when dependencies change
149
+ console.log('Data changed:', data.value)
150
+ })
151
+ </script>
152
+ ```
153
+
154
+ ## Reference
155
+ - [Composition API FAQ - Relationship with React Hooks](https://vuejs.org/guide/extras/composition-api-faq.html#relationship-with-react-hooks)
156
+ - [Reactivity Fundamentals](https://vuejs.org/guide/essentials/reactivity-fundamentals.html)