@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,124 @@
1
+ ---
2
+ title: v-model.number Uses parseFloat Not valueAsNumber
3
+ impact: MEDIUM
4
+ impactDescription: .number modifier returns empty string for empty input and uses parseFloat, not native valueAsNumber
5
+ type: capability
6
+ tags: [vue3, v-model, forms, input, number, type-coercion, modifiers]
7
+ ---
8
+
9
+ # v-model.number Uses parseFloat Not valueAsNumber
10
+
11
+ **Impact: MEDIUM** - The `.number` modifier doesn't behave like the native `valueAsNumber` property. It returns an empty string (not NaN) for empty inputs, and uses `parseFloat()` which has different parsing rules. This can cause unexpected type issues in calculations and validations.
12
+
13
+ Understanding these differences is crucial when working with numeric forms, especially for calculations, min/max validation, or when interfacing with APIs that expect strict number types.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Expect empty string (not 0 or NaN) when input is cleared with `.number` modifier
18
+ - [ ] Handle the empty string case in your validation and calculations
19
+ - [ ] Remember `.number` uses parseFloat - "123abc" becomes 123, not NaN
20
+ - [ ] For strict numeric parsing, add custom validation
21
+
22
+ **Key Differences:**
23
+
24
+ | Scenario | `.number` modifier | Native `valueAsNumber` |
25
+ |----------|-------------------|----------------------|
26
+ | Empty input | `''` (empty string) | `NaN` |
27
+ | `"123"` | `123` | `123` |
28
+ | `"123.45"` | `123.45` | `123.45` |
29
+ | `"123abc"` | `123` | `NaN` |
30
+ | `"abc"` | `'abc'` (original string) | `NaN` |
31
+
32
+ **Problem - Unexpected types:**
33
+ ```html
34
+ <script setup>
35
+ import { ref, computed } from 'vue'
36
+
37
+ const price = ref(0)
38
+ const quantity = ref(1)
39
+
40
+ const total = computed(() => {
41
+ // PROBLEM: price might be '' (empty string) when input is cleared
42
+ return price.value * quantity.value // '' * 1 = 0, but typeof is still number
43
+ })
44
+
45
+ function validatePrice() {
46
+ // PROBLEM: This check fails when input is empty
47
+ if (typeof price.value !== 'number') {
48
+ // Never enters here! '' is still treated as "processed"
49
+ }
50
+
51
+ // PROBLEM: parseFloat("$100") returns NaN, but "100abc" returns 100
52
+ }
53
+ </script>
54
+
55
+ <template>
56
+ <input v-model.number="price" type="number">
57
+ <!-- When user clears input, price.value becomes '' not 0 or NaN -->
58
+ </template>
59
+ ```
60
+
61
+ **Solution - Handle empty string explicitly:**
62
+ ```html
63
+ <script setup>
64
+ import { ref, computed } from 'vue'
65
+
66
+ const price = ref(0)
67
+ const quantity = ref(1)
68
+
69
+ const total = computed(() => {
70
+ // CORRECT: Handle empty string case
71
+ const priceNum = price.value === '' ? 0 : price.value
72
+ return priceNum * quantity.value
73
+ })
74
+
75
+ // Or use a wrapper computed for safer access
76
+ const safePrice = computed(() => {
77
+ if (price.value === '' || price.value === null) return 0
78
+ return Number(price.value) || 0
79
+ })
80
+ </script>
81
+
82
+ <template>
83
+ <input v-model.number="price" type="number" min="0">
84
+ <p>Total: ${{ total.toFixed(2) }}</p>
85
+ </template>
86
+ ```
87
+
88
+ **Solution - Custom input handling for strict parsing:**
89
+ ```html
90
+ <script setup>
91
+ import { ref } from 'vue'
92
+
93
+ const price = ref(0)
94
+
95
+ function handlePriceInput(event) {
96
+ const value = event.target.value
97
+
98
+ // Strict parsing - only accept valid numbers
99
+ const parsed = parseFloat(value)
100
+
101
+ if (value === '') {
102
+ price.value = 0 // Or null, depending on your needs
103
+ } else if (!isNaN(parsed) && isFinite(parsed)) {
104
+ // Additional check: ensure entire string is numeric
105
+ if (/^-?\d*\.?\d+$/.test(value.trim())) {
106
+ price.value = parsed
107
+ }
108
+ }
109
+ // Invalid input - keep previous value
110
+ }
111
+ </script>
112
+
113
+ <template>
114
+ <!-- Manual binding for strict numeric control -->
115
+ <input
116
+ :value="price"
117
+ @input="handlePriceInput"
118
+ type="number"
119
+ >
120
+ </template>
121
+ ```
122
+
123
+ ## Reference
124
+ - [Vue.js Form Input Bindings - .number](https://vuejs.org/guide/essentials/forms.html#number)
@@ -0,0 +1,124 @@
1
+ ---
2
+ title: v-show Does Not Work on template or With v-else
3
+ impact: MEDIUM
4
+ impactDescription: Using v-show on template silently fails, element is always rendered
5
+ type: capability
6
+ tags: [vue3, conditional-rendering, v-show, template, limitations]
7
+ ---
8
+
9
+ # v-show Does Not Work on template or With v-else
10
+
11
+ **Impact: MEDIUM** - `v-show` cannot be used on `<template>` elements because templates don't render to the DOM (so there's nothing to apply `display: none` to). Additionally, `v-show` does not support `v-else`. Using these incorrectly results in elements that are always visible or else branches that never work.
12
+
13
+ ## Task Checklist
14
+
15
+ - [ ] Never use v-show on `<template>` elements - use v-if instead
16
+ - [ ] Never use v-else with v-show - use separate v-show with negated condition
17
+ - [ ] Remember v-show only works on actual DOM elements
18
+ - [ ] If you need to toggle multiple elements frequently, wrap in a real element (div, span)
19
+
20
+ **Incorrect:**
21
+ ```html
22
+ <!-- WRONG: v-show on <template> - silently does nothing -->
23
+ <template>
24
+ <template v-show="isVisible">
25
+ <h1>Title</h1>
26
+ <p>Content</p>
27
+ </template>
28
+ <!-- These elements will ALWAYS be visible -->
29
+ </template>
30
+ ```
31
+
32
+ ```html
33
+ <!-- WRONG: v-else with v-show - v-else is not supported -->
34
+ <template>
35
+ <div v-show="isLoggedIn">Welcome!</div>
36
+ <div v-else>Please log in</div> <!-- This v-else won't work -->
37
+ </template>
38
+ ```
39
+
40
+ ```html
41
+ <!-- WRONG: Mixing v-show and v-else expectations -->
42
+ <template>
43
+ <span v-show="status === 'success'">Success!</span>
44
+ <span v-else-if="status === 'error'">Error</span> <!-- Not supported -->
45
+ </template>
46
+ ```
47
+
48
+ **Correct:**
49
+ ```html
50
+ <!-- CORRECT: Use v-if on <template> for multiple elements -->
51
+ <template>
52
+ <template v-if="isVisible">
53
+ <h1>Title</h1>
54
+ <p>Content</p>
55
+ </template>
56
+ </template>
57
+ ```
58
+
59
+ ```html
60
+ <!-- CORRECT: Use negated v-show for "else" behavior -->
61
+ <template>
62
+ <div v-show="isLoggedIn">Welcome!</div>
63
+ <div v-show="!isLoggedIn">Please log in</div>
64
+ </template>
65
+ ```
66
+
67
+ ```html
68
+ <!-- CORRECT: Wrap in a real element if you need v-show for multiple elements -->
69
+ <template>
70
+ <div v-show="isVisible">
71
+ <h1>Title</h1>
72
+ <p>Content</p>
73
+ </div>
74
+ </template>
75
+ ```
76
+
77
+ ```html
78
+ <!-- CORRECT: Use v-if/v-else when you need else branches -->
79
+ <template>
80
+ <div v-if="status === 'success'">Success!</div>
81
+ <div v-else-if="status === 'error'">Error</div>
82
+ <div v-else>Loading...</div>
83
+ </template>
84
+ ```
85
+
86
+ ```html
87
+ <!-- CORRECT: Multiple v-show conditions -->
88
+ <template>
89
+ <span v-show="status === 'success'">Success!</span>
90
+ <span v-show="status === 'error'">Error</span>
91
+ <span v-show="status === 'loading'">Loading...</span>
92
+ </template>
93
+ ```
94
+
95
+ ## Why This Limitation Exists
96
+
97
+ ```javascript
98
+ // v-show works by toggling the CSS display property
99
+ // This requires an actual DOM element
100
+
101
+ // <template> is a virtual element - it doesn't render to DOM
102
+ // It's just a wrapper for Vue's rendering logic
103
+
104
+ // After compilation:
105
+ // <template v-if="show"><p>Hi</p></template>
106
+ // Renders as: <p>Hi</p> (when show is true)
107
+ // Template itself is gone
108
+
109
+ // v-show needs a real element to set display: none on
110
+ // Since <template> doesn't exist in DOM, v-show has nothing to work with
111
+ ```
112
+
113
+ ## When to Choose Each
114
+
115
+ | Need | Use |
116
+ |------|-----|
117
+ | Toggle multiple elements with CSS | Wrap in real element + `v-show` |
118
+ | Toggle multiple elements without wrapper | `<template v-if>` |
119
+ | Need v-else branches | `v-if`/`v-else` |
120
+ | Frequent toggle, single element | `v-show` |
121
+ | Frequent toggle, need "else" | Two `v-show` with negated conditions |
122
+
123
+ ## Reference
124
+ - [Vue.js Conditional Rendering - v-show](https://vuejs.org/guide/essentials/conditional.html#v-show)
@@ -0,0 +1,180 @@
1
+ ---
2
+ title: Clean Up Async Operations in Watchers to Prevent Race Conditions
3
+ impact: HIGH
4
+ impactDescription: Stale async requests can overwrite newer data, causing incorrect UI state and hard-to-debug issues
5
+ type: capability
6
+ tags: [vue3, watch, watchers, async, cleanup, race-condition, abort]
7
+ ---
8
+
9
+ # Clean Up Async Operations in Watchers to Prevent Race Conditions
10
+
11
+ **Impact: HIGH** - When a watched value changes rapidly, multiple async operations run concurrently. Without cleanup, a slow earlier request can complete after a faster later request, overwriting current data with stale results.
12
+
13
+ Always use `onWatcherCleanup` or the `onCleanup` callback parameter to cancel pending async operations when the watcher re-runs or the component unmounts.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Use `onWatcherCleanup()` or `onCleanup` parameter in async watchers
18
+ - [ ] Use `AbortController` to cancel pending fetch requests
19
+ - [ ] Cancel any setTimeout/setInterval calls in cleanup
20
+ - [ ] Invalidate previous async operation results with flags
21
+ - [ ] Consider debouncing rapid changes before fetching
22
+
23
+ **Incorrect:**
24
+ ```javascript
25
+ import { ref, watch } from 'vue'
26
+
27
+ const searchQuery = ref('')
28
+ const results = ref([])
29
+
30
+ // BAD: Race condition - slow request for "a" can finish after fast request for "ab"
31
+ watch(searchQuery, async (query) => {
32
+ if (query) {
33
+ const response = await fetch(`/api/search?q=${query}`)
34
+ results.value = await response.json() // May overwrite newer results!
35
+ }
36
+ })
37
+
38
+ // BAD: No cleanup for timeouts
39
+ watch(searchQuery, (query) => {
40
+ // Previous timeout keeps running even when query changes
41
+ setTimeout(() => {
42
+ performExpensiveSearch(query)
43
+ }, 500)
44
+ })
45
+ ```
46
+
47
+ **Correct:**
48
+ ```javascript
49
+ import { ref, watch, onWatcherCleanup } from 'vue'
50
+
51
+ const searchQuery = ref('')
52
+ const results = ref([])
53
+ const loading = ref(false)
54
+
55
+ // CORRECT: Using onWatcherCleanup (Vue 3.5+)
56
+ watch(searchQuery, async (query) => {
57
+ if (!query) {
58
+ results.value = []
59
+ return
60
+ }
61
+
62
+ const controller = new AbortController()
63
+
64
+ // Register cleanup to abort on re-run or unmount
65
+ onWatcherCleanup(() => {
66
+ controller.abort()
67
+ })
68
+
69
+ loading.value = true
70
+ try {
71
+ const response = await fetch(`/api/search?q=${query}`, {
72
+ signal: controller.signal
73
+ })
74
+ results.value = await response.json()
75
+ } catch (err) {
76
+ if (err.name !== 'AbortError') {
77
+ console.error('Search failed:', err)
78
+ }
79
+ } finally {
80
+ loading.value = false
81
+ }
82
+ })
83
+ ```
84
+
85
+ ## Using onCleanup Parameter
86
+
87
+ ```javascript
88
+ import { ref, watch } from 'vue'
89
+
90
+ const userId = ref(1)
91
+ const userData = ref(null)
92
+
93
+ // CORRECT: Using onCleanup callback parameter
94
+ watch(userId, (newId, oldId, onCleanup) => {
95
+ const controller = new AbortController()
96
+
97
+ fetch(`/api/users/${newId}`, { signal: controller.signal })
98
+ .then(res => res.json())
99
+ .then(data => {
100
+ userData.value = data
101
+ })
102
+ .catch(err => {
103
+ if (err.name !== 'AbortError') {
104
+ console.error(err)
105
+ }
106
+ })
107
+
108
+ onCleanup(() => {
109
+ controller.abort()
110
+ })
111
+ })
112
+ ```
113
+
114
+ ## Cleanup with Timeouts
115
+
116
+ ```javascript
117
+ import { ref, watch, onWatcherCleanup } from 'vue'
118
+
119
+ const input = ref('')
120
+
121
+ // CORRECT: Cancel previous timeout on new input
122
+ watch(input, (value) => {
123
+ const timeoutId = setTimeout(() => {
124
+ performExpensiveOperation(value)
125
+ }, 300)
126
+
127
+ onWatcherCleanup(() => {
128
+ clearTimeout(timeoutId)
129
+ })
130
+ })
131
+ ```
132
+
133
+ ## Invalidation Flag Pattern
134
+
135
+ ```javascript
136
+ import { ref, watch } from 'vue'
137
+
138
+ const id = ref(1)
139
+ const data = ref(null)
140
+
141
+ // CORRECT: Invalidation flag for non-abortable operations
142
+ watch(id, async (newId, oldId, onCleanup) => {
143
+ let cancelled = false
144
+
145
+ onCleanup(() => {
146
+ cancelled = true
147
+ })
148
+
149
+ const result = await someNonAbortableAsyncOperation(newId)
150
+
151
+ // Check if this watch run is still valid
152
+ if (!cancelled) {
153
+ data.value = result
154
+ }
155
+ })
156
+ ```
157
+
158
+ ## watchEffect Cleanup
159
+
160
+ ```javascript
161
+ import { ref, watchEffect, onWatcherCleanup } from 'vue'
162
+
163
+ const resourceId = ref('abc')
164
+
165
+ watchEffect(async () => {
166
+ const id = resourceId.value
167
+ const controller = new AbortController()
168
+
169
+ onWatcherCleanup(() => {
170
+ controller.abort()
171
+ })
172
+
173
+ const data = await fetchResource(id, { signal: controller.signal })
174
+ processData(data)
175
+ })
176
+ ```
177
+
178
+ ## Reference
179
+ - [Vue.js Watchers - Callback Flush Timing](https://vuejs.org/guide/essentials/watchers.html#callback-flush-timing)
180
+ - [Vue.js Watchers - Side Effect Cleanup](https://vuejs.org/api/reactivity-core.html#watcheffect)
@@ -0,0 +1,176 @@
1
+ ---
2
+ title: Watchers Created Asynchronously Must Be Manually Stopped
3
+ impact: HIGH
4
+ impactDescription: Async-created watchers are not bound to component lifecycle and cause memory leaks
5
+ type: capability
6
+ tags: [vue3, watch, watchers, async, memory-leak, lifecycle, cleanup]
7
+ ---
8
+
9
+ # Watchers Created Asynchronously Must Be Manually Stopped
10
+
11
+ **Impact: HIGH** - Watchers created inside async callbacks (setTimeout, Promise.then, async/await) are not automatically bound to the component instance. They continue running after the component unmounts, causing memory leaks and errors.
12
+
13
+ Always manually stop watchers that are created asynchronously, or restructure your code to create watchers synchronously with conditional logic.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Create watchers synchronously in setup() or lifecycle hooks when possible
18
+ - [ ] If async creation is unavoidable, store and call the unwatch function
19
+ - [ ] Use `onUnmounted` to clean up async-created watchers
20
+ - [ ] Consider using conditional logic inside a sync watcher instead
21
+ - [ ] Watch for this pattern in setTimeout, Promise callbacks, and after await
22
+
23
+ **Incorrect:**
24
+ ```vue
25
+ <script setup>
26
+ import { ref, watch, watchEffect, onMounted } from 'vue'
27
+
28
+ const data = ref(null)
29
+
30
+ // BAD: Watcher created in setTimeout won't auto-stop
31
+ onMounted(() => {
32
+ setTimeout(() => {
33
+ watchEffect(() => {
34
+ console.log(data.value) // Keeps running after unmount!
35
+ })
36
+ }, 1000)
37
+ })
38
+
39
+ // BAD: Watcher created after await won't auto-stop
40
+ onMounted(async () => {
41
+ await loadInitialData()
42
+
43
+ // This watcher is NOT bound to component lifecycle
44
+ watch(data, (newVal) => {
45
+ processData(newVal) // Memory leak!
46
+ })
47
+ })
48
+
49
+ // BAD: Watcher in Promise callback
50
+ fetch('/api/config').then(() => {
51
+ watch(data, () => {
52
+ // Leaks memory!
53
+ })
54
+ })
55
+ </script>
56
+ ```
57
+
58
+ **Correct:**
59
+ ```vue
60
+ <script setup>
61
+ import { ref, watch, watchEffect, onMounted, onUnmounted } from 'vue'
62
+
63
+ const data = ref(null)
64
+ const isDataLoaded = ref(false)
65
+ let asyncWatcherCleanup = null
66
+
67
+ // CORRECT: Synchronous watcher with conditional logic
68
+ watch(
69
+ data,
70
+ (newVal) => {
71
+ if (isDataLoaded.value && newVal) {
72
+ processData(newVal)
73
+ }
74
+ }
75
+ )
76
+
77
+ onMounted(async () => {
78
+ await loadInitialData()
79
+ isDataLoaded.value = true
80
+ })
81
+
82
+ // CORRECT: Manual cleanup for async-created watcher
83
+ onMounted(() => {
84
+ setTimeout(() => {
85
+ const unwatch = watchEffect(() => {
86
+ console.log(data.value)
87
+ })
88
+
89
+ // Store for cleanup
90
+ asyncWatcherCleanup = unwatch
91
+ }, 1000)
92
+ })
93
+
94
+ onUnmounted(() => {
95
+ // Clean up async watcher
96
+ if (asyncWatcherCleanup) {
97
+ asyncWatcherCleanup()
98
+ }
99
+ })
100
+ </script>
101
+ ```
102
+
103
+ ## Preferred Pattern: Conditional Watch Logic
104
+
105
+ ```vue
106
+ <script setup>
107
+ import { ref, watch, onMounted } from 'vue'
108
+
109
+ const config = ref(null)
110
+ const userData = ref(null)
111
+
112
+ // BEST: Create watcher synchronously, handle async condition inside
113
+ watch(
114
+ userData,
115
+ (newData) => {
116
+ // Only process when config is loaded
117
+ if (config.value && newData) {
118
+ applyUserSettings(config.value, newData)
119
+ }
120
+ }
121
+ )
122
+
123
+ onMounted(async () => {
124
+ config.value = await fetchConfig()
125
+ // Watcher will start processing once config is loaded
126
+ })
127
+ </script>
128
+ ```
129
+
130
+ ## Using watchEffect with Conditional Logic
131
+
132
+ ```vue
133
+ <script setup>
134
+ import { ref, watchEffect, onMounted } from 'vue'
135
+
136
+ const apiData = ref(null)
137
+ const isReady = ref(false)
138
+
139
+ // GOOD: Synchronous watchEffect with condition
140
+ watchEffect(() => {
141
+ if (isReady.value && apiData.value) {
142
+ // This pattern avoids async watcher creation
143
+ doSomethingWithData(apiData.value)
144
+ }
145
+ })
146
+
147
+ onMounted(async () => {
148
+ apiData.value = await fetchData()
149
+ isReady.value = true
150
+ })
151
+ </script>
152
+ ```
153
+
154
+ ## Tracking Multiple Async Watchers
155
+
156
+ ```vue
157
+ <script setup>
158
+ import { ref, watch, onUnmounted } from 'vue'
159
+
160
+ const unwatchers = []
161
+
162
+ function createDynamicWatcher(source, callback) {
163
+ const unwatch = watch(source, callback)
164
+ unwatchers.push(unwatch)
165
+ return unwatch
166
+ }
167
+
168
+ // Clean up all dynamic watchers
169
+ onUnmounted(() => {
170
+ unwatchers.forEach(unwatch => unwatch())
171
+ })
172
+ </script>
173
+ ```
174
+
175
+ ## Reference
176
+ - [Vue.js Watchers - Stopping a Watcher](https://vuejs.org/guide/essentials/watchers.html#stopping-a-watcher)