@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,139 @@
1
+ ---
2
+ title: defineModel Default Value Can Cause Parent-Child Desync
3
+ impact: HIGH
4
+ impactDescription: Default values in defineModel don't sync back to parent, causing state inconsistency
5
+ type: capability
6
+ tags: [vue3, v-model, defineModel, components, props, two-way-binding]
7
+ ---
8
+
9
+ # defineModel Default Value Can Cause Parent-Child Desync
10
+
11
+ **Impact: HIGH** - When using `defineModel()` with a default value and the parent doesn't provide a value, the parent and child components will have different values. The parent's ref stays `undefined` while the child uses the default, breaking the two-way binding contract.
12
+
13
+ This subtle bug can cause confusing behavior where the parent component shows one value while the child shows another, and updates may not propagate correctly.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Always provide initial values from the parent when using v-model
18
+ - [ ] Don't rely on defineModel defaults as the primary source of truth
19
+ - [ ] If defaults are needed, also set them in the parent component
20
+ - [ ] Test components with and without v-model props provided
21
+
22
+ **Problem - Parent and child out of sync:**
23
+ ```html
24
+ <!-- ChildComponent.vue -->
25
+ <script setup>
26
+ // Default value of 1 if parent doesn't provide value
27
+ const model = defineModel({ default: 1 })
28
+ </script>
29
+
30
+ <template>
31
+ <input v-model="model" type="number">
32
+ <!-- Shows: 1 (from default) -->
33
+ </template>
34
+ ```
35
+
36
+ ```html
37
+ <!-- ParentComponent.vue -->
38
+ <script setup>
39
+ import { ref } from 'vue'
40
+ import ChildComponent from './ChildComponent.vue'
41
+
42
+ // PROBLEM: Parent ref is undefined, not synced with child's default
43
+ const myValue = ref() // undefined
44
+ </script>
45
+
46
+ <template>
47
+ <ChildComponent v-model="myValue" />
48
+
49
+ <!-- DESYNC: Child shows 1, but parent shows undefined -->
50
+ <p>Parent value: {{ myValue }}</p> <!-- Shows: undefined -->
51
+
52
+ <!-- Even after child changes value, parent may not update correctly -->
53
+ </template>
54
+ ```
55
+
56
+ **Solution 1 - Always provide initial value from parent:**
57
+ ```html
58
+ <!-- ParentComponent.vue -->
59
+ <script setup>
60
+ import { ref } from 'vue'
61
+ import ChildComponent from './ChildComponent.vue'
62
+
63
+ // CORRECT: Parent provides the initial value
64
+ const myValue = ref(1) // Match the expected default
65
+ </script>
66
+
67
+ <template>
68
+ <ChildComponent v-model="myValue" />
69
+ <p>Parent value: {{ myValue }}</p> <!-- Shows: 1, stays in sync -->
70
+ </template>
71
+ ```
72
+
73
+ **Solution 2 - Child emits default on mount (if parent control not possible):**
74
+ ```html
75
+ <!-- ChildComponent.vue -->
76
+ <script setup>
77
+ import { onMounted } from 'vue'
78
+
79
+ const model = defineModel({ default: 1 })
80
+
81
+ // Sync default value back to parent on mount
82
+ onMounted(() => {
83
+ if (model.value === 1) { // Is using default
84
+ // Force emit to sync parent
85
+ model.value = 1
86
+ }
87
+ })
88
+ </script>
89
+
90
+ <template>
91
+ <input v-model="model" type="number">
92
+ </template>
93
+ ```
94
+
95
+ **Solution 3 - Use required prop or explicit undefined handling:**
96
+ ```html
97
+ <!-- ChildComponent.vue -->
98
+ <script setup>
99
+ import { computed } from 'vue'
100
+
101
+ // Mark as required - TypeScript will warn if not provided
102
+ const model = defineModel({ required: true })
103
+
104
+ // Or handle undefined explicitly
105
+ const safeModel = computed({
106
+ get: () => model.value ?? 1, // Provide fallback
107
+ set: (val) => { model.value = val }
108
+ })
109
+ </script>
110
+
111
+ <template>
112
+ <input v-model="safeModel" type="number">
113
+ </template>
114
+ ```
115
+
116
+ **Best Practice - Document expected initial values:**
117
+ ```html
118
+ <!-- ChildComponent.vue -->
119
+ <script setup>
120
+ /**
121
+ * @prop modelValue - The numeric value (parent should initialize to 1 or desired default)
122
+ */
123
+ const model = defineModel({
124
+ type: Number,
125
+ default: 1,
126
+ // Adding validator helps catch issues in development
127
+ validator: (value) => {
128
+ if (value === undefined) {
129
+ console.warn('ChildComponent: v-model value is undefined. Provide initial value from parent.')
130
+ }
131
+ return true
132
+ }
133
+ })
134
+ </script>
135
+ ```
136
+
137
+ ## Reference
138
+ - [Vue.js Component v-model](https://vuejs.org/guide/components/v-model.html)
139
+ - [Vue School - defineModel Guide](https://vueschool.io/articles/vuejs-tutorials/v-model-and-definemodel-a-comprehensive-guide-to-two-way-binding-in-vue-js-3/)
@@ -0,0 +1,164 @@
1
+ ---
2
+ title: defineEmits Must Be Used at Top Level of script setup
3
+ impact: HIGH
4
+ impactDescription: Using defineEmits inside functions causes compilation errors - macros must be at module scope
5
+ type: gotcha
6
+ tags: [vue3, defineEmits, script-setup, macros, composition-api]
7
+ ---
8
+
9
+ # defineEmits Must Be Used at Top Level of script setup
10
+
11
+ **Impact: HIGH** - The `defineEmits()` macro can only be used directly within `<script setup>` at the top level. It cannot be placed inside functions, conditionals, or any other nested scope. Vue's compiler hoists these macros to module scope during compilation.
12
+
13
+ This applies to all Vue macros: `defineProps`, `defineEmits`, `defineExpose`, `defineOptions`, and `defineSlots`.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Place `defineEmits()` directly in `<script setup>`, not inside functions
18
+ - [ ] Do not wrap macro calls in conditionals or loops
19
+ - [ ] Do not reference local variables in macro arguments
20
+ - [ ] Store the emit function and reuse it throughout the component
21
+
22
+ ## The Problem
23
+
24
+ **Incorrect - Inside a function:**
25
+ ```vue
26
+ <script setup>
27
+ function useEvents() {
28
+ // ERROR: defineEmits cannot be used inside a function
29
+ const emit = defineEmits(['submit', 'cancel'])
30
+ return emit
31
+ }
32
+
33
+ const emit = useEvents() // This fails at compile time
34
+ </script>
35
+ ```
36
+
37
+ **Incorrect - Inside a conditional:**
38
+ ```vue
39
+ <script setup>
40
+ if (someCondition) {
41
+ // ERROR: Cannot use defineEmits in conditional
42
+ const emit = defineEmits(['eventA'])
43
+ } else {
44
+ const emit = defineEmits(['eventB'])
45
+ }
46
+ </script>
47
+ ```
48
+
49
+ **Incorrect - Referencing local variables:**
50
+ ```vue
51
+ <script setup>
52
+ const eventNames = ['submit', 'cancel']
53
+
54
+ // ERROR: Cannot reference local variables
55
+ const emit = defineEmits(eventNames)
56
+ </script>
57
+ ```
58
+
59
+ ## Correct Usage
60
+
61
+ **Correct - Top level declaration:**
62
+ ```vue
63
+ <script setup>
64
+ // CORRECT: defineEmits at top level of script setup
65
+ const emit = defineEmits(['submit', 'cancel', 'update'])
66
+
67
+ function handleSubmit() {
68
+ emit('submit', data)
69
+ }
70
+
71
+ function handleCancel() {
72
+ emit('cancel')
73
+ }
74
+ </script>
75
+ ```
76
+
77
+ **Correct - With TypeScript types:**
78
+ ```vue
79
+ <script setup lang="ts">
80
+ // CORRECT: Type-based declaration at top level
81
+ const emit = defineEmits<{
82
+ submit: [data: FormData]
83
+ cancel: []
84
+ 'update:modelValue': [value: string]
85
+ }>()
86
+
87
+ function handleSubmit(data: FormData) {
88
+ emit('submit', data)
89
+ }
90
+ </script>
91
+ ```
92
+
93
+ **Correct - Using constant arrays (compile-time constant):**
94
+ ```vue
95
+ <script setup>
96
+ // CORRECT: Literal array is fine
97
+ const emit = defineEmits(['submit', 'cancel'])
98
+ </script>
99
+ ```
100
+
101
+ ## Why This Restriction Exists
102
+
103
+ Vue's compiler processes `<script setup>` macros at compile time, not runtime. The arguments must be statically analyzable so Vue can:
104
+
105
+ 1. Generate the correct component options
106
+ 2. Provide TypeScript type inference
107
+ 3. Enable IDE support for event autocompletion
108
+ 4. Validate emitted events
109
+
110
+ Since the macro is hoisted out of `<script setup>` during compilation, it cannot access anything that only exists at runtime.
111
+
112
+ ## Using emit in Composables
113
+
114
+ If you want to share emit logic in a composable, pass the emit function as an argument:
115
+
116
+ **Correct - Pass emit to composable:**
117
+ ```vue
118
+ <script setup>
119
+ const emit = defineEmits(['submit', 'cancel', 'validate'])
120
+
121
+ // Pass emit to composable
122
+ const { handleSubmit, handleCancel } = useFormEvents(emit)
123
+ </script>
124
+ ```
125
+
126
+ ```js
127
+ // composables/useFormEvents.js
128
+ export function useFormEvents(emit) {
129
+ function handleSubmit(data) {
130
+ emit('submit', data)
131
+ }
132
+
133
+ function handleCancel() {
134
+ emit('cancel')
135
+ }
136
+
137
+ return { handleSubmit, handleCancel }
138
+ }
139
+ ```
140
+
141
+ ## ESLint Rule
142
+
143
+ The `eslint-plugin-vue` provides the `vue/valid-define-emits` rule that catches these errors:
144
+
145
+ ```js
146
+ // eslint.config.js
147
+ export default [
148
+ {
149
+ rules: {
150
+ 'vue/valid-define-emits': 'error'
151
+ }
152
+ }
153
+ ]
154
+ ```
155
+
156
+ This rule reports:
157
+ - `defineEmits` used inside functions
158
+ - `defineEmits` referencing local variables
159
+ - Multiple `defineEmits` calls in the same component
160
+ - `defineEmits` used outside `<script setup>`
161
+
162
+ ## Reference
163
+ - [Vue.js SFC script setup](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)
164
+ - [ESLint vue/valid-define-emits](https://eslint.vuejs.org/rules/valid-define-emits)
@@ -0,0 +1,170 @@
1
+ ---
2
+ title: Cannot Mix Runtime and Type Declarations in defineEmits
3
+ impact: HIGH
4
+ impactDescription: Using both array/object syntax AND TypeScript generics in defineEmits causes compile errors
5
+ type: gotcha
6
+ tags: [vue3, defineEmits, typescript, compilation-error, script-setup]
7
+ ---
8
+
9
+ # Cannot Mix Runtime and Type Declarations in defineEmits
10
+
11
+ **Impact: HIGH** - `defineEmits` supports two declaration styles: runtime (array/object syntax) and type-based (TypeScript generics). You CANNOT use both at the same time. Attempting to do so results in a compile-time error.
12
+
13
+ This is a common mistake when learning Vue 3 with TypeScript.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Choose ONE declaration style: runtime OR type-based
18
+ - [ ] For TypeScript projects, prefer type-based declaration
19
+ - [ ] For JavaScript projects, use runtime (array/object) declaration
20
+ - [ ] Never pass arguments when using generic type parameter
21
+
22
+ ## The Problem
23
+
24
+ **Incorrect - Mixing both styles:**
25
+ ```vue
26
+ <script setup lang="ts">
27
+ // ERROR: Cannot use both type argument and runtime argument
28
+ const emit = defineEmits<{
29
+ submit: [data: FormData]
30
+ }>(['submit']) // This array argument causes the error!
31
+ </script>
32
+ ```
33
+
34
+ **Compiler error:**
35
+ ```
36
+ defineEmits() cannot accept both type and non-type arguments at the same time.
37
+ Use one or the other.
38
+ ```
39
+
40
+ **Also incorrect:**
41
+ ```vue
42
+ <script setup lang="ts">
43
+ // ERROR: Same problem with object syntax
44
+ const emit = defineEmits<{
45
+ submit: [data: FormData]
46
+ }>({
47
+ submit: (data) => !!data
48
+ })
49
+ </script>
50
+ ```
51
+
52
+ ## Correct: Type-Based Declaration (TypeScript)
53
+
54
+ ```vue
55
+ <script setup lang="ts">
56
+ // CORRECT: Type argument only, no runtime argument
57
+ const emit = defineEmits<{
58
+ submit: [data: FormData]
59
+ cancel: []
60
+ 'update:modelValue': [value: string]
61
+ }>()
62
+
63
+ emit('submit', formData) // TypeScript validates this
64
+ emit('cancel')
65
+ emit('unknown') // TypeScript error: unknown event
66
+ </script>
67
+ ```
68
+
69
+ **Alternative call signature syntax:**
70
+ ```vue
71
+ <script setup lang="ts">
72
+ const emit = defineEmits<{
73
+ (e: 'submit', data: FormData): void
74
+ (e: 'cancel'): void
75
+ (e: 'update:modelValue', value: string): void
76
+ }>()
77
+ </script>
78
+ ```
79
+
80
+ ## Correct: Runtime Declaration (JavaScript or Simple Cases)
81
+
82
+ **Array syntax:**
83
+ ```vue
84
+ <script setup>
85
+ // CORRECT: Runtime array, no type argument
86
+ const emit = defineEmits(['submit', 'cancel', 'update:modelValue'])
87
+
88
+ emit('submit', formData)
89
+ emit('cancel')
90
+ </script>
91
+ ```
92
+
93
+ **Object syntax with validation:**
94
+ ```vue
95
+ <script setup>
96
+ // CORRECT: Runtime object for validation
97
+ const emit = defineEmits({
98
+ submit: (data) => {
99
+ if (!data?.email) {
100
+ console.warn('Missing email')
101
+ return false
102
+ }
103
+ return true
104
+ },
105
+ cancel: null // No validation
106
+ })
107
+ </script>
108
+ ```
109
+
110
+ ## Adding Validation to Type-Based Emits
111
+
112
+ If you want TypeScript types AND runtime validation, define the validator separately:
113
+
114
+ ```vue
115
+ <script setup lang="ts">
116
+ interface FormData {
117
+ email: string
118
+ message: string
119
+ }
120
+
121
+ // Type-based declaration for TypeScript
122
+ const emit = defineEmits<{
123
+ submit: [data: FormData]
124
+ }>()
125
+
126
+ // Separate validation function
127
+ function emitSubmit(data: FormData) {
128
+ if (!data.email.includes('@')) {
129
+ console.warn('Invalid email format')
130
+ return
131
+ }
132
+ emit('submit', data)
133
+ }
134
+ </script>
135
+
136
+ <template>
137
+ <button @click="emitSubmit(formData)">Submit</button>
138
+ </template>
139
+ ```
140
+
141
+ ## Choosing Between Styles
142
+
143
+ | Style | Use When | Benefits |
144
+ |-------|----------|----------|
145
+ | Type-based | TypeScript project | Compile-time checking, IDE support |
146
+ | Array | JavaScript, simple events | Simple, no types needed |
147
+ | Object | Need runtime validation | Validates payloads at runtime |
148
+
149
+ **Recommendation:** In TypeScript projects, use type-based declaration. It provides the best developer experience with autocompletion and type checking.
150
+
151
+ ## Same Rule Applies to defineProps
152
+
153
+ This restriction also applies to `defineProps`:
154
+
155
+ ```vue
156
+ <script setup lang="ts">
157
+ // ERROR: Cannot mix
158
+ const props = defineProps<{ name: string }>({ name: String })
159
+
160
+ // CORRECT: Type-based only
161
+ const props = defineProps<{ name: string }>()
162
+
163
+ // CORRECT: Runtime only
164
+ const props = defineProps({ name: String })
165
+ </script>
166
+ ```
167
+
168
+ ## Reference
169
+ - [Vue.js SFC script setup - defineEmits](https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits)
170
+ - [Vue.js TypeScript with Composition API](https://vuejs.org/guide/typescript/composition-api.html#typing-component-emits)
@@ -0,0 +1,148 @@
1
+ ---
2
+ title: defineModel Object Properties Must Be Replaced, Not Mutated
3
+ impact: HIGH
4
+ impactDescription: Mutating object properties via defineModel doesn't emit update events, breaking parent sync
5
+ type: gotcha
6
+ tags: [vue3, v-model, defineModel, objects, reactivity, two-way-binding]
7
+ ---
8
+
9
+ # defineModel Object Properties Must Be Replaced, Not Mutated
10
+
11
+ **Impact: HIGH** - When using `defineModel()` with objects or arrays, directly mutating nested properties like `model.value.prop = x` does NOT emit the `update:modelValue` event. The parent component never receives the change notification, causing silent sync failures.
12
+
13
+ This happens because Vue only detects when the `model.value` reference itself changes, not when properties of the object are mutated in place.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Never mutate object properties directly: `model.value.prop = x`
18
+ - [ ] Always create a new object reference when updating: `model.value = {...model.value, prop: x}`
19
+ - [ ] For arrays, use spread or slice: `model.value = [...model.value, newItem]`
20
+ - [ ] Consider using structuredClone for deeply nested objects
21
+
22
+ **Incorrect - Mutation without event emission:**
23
+ ```vue
24
+ <script setup>
25
+ // Child component with object v-model
26
+ const model = defineModel<{ name: string; age: number }>()
27
+
28
+ function updateName(newName: string) {
29
+ // WRONG: This mutates the object in place
30
+ // Parent receives NO update:modelValue event!
31
+ model.value.name = newName
32
+ }
33
+
34
+ function addToList() {
35
+ // WRONG: Push mutates the array
36
+ model.value.items.push('new item') // Parent not notified
37
+ }
38
+ </script>
39
+ ```
40
+
41
+ **Correct - Replace object reference to trigger event:**
42
+ ```vue
43
+ <script setup>
44
+ const model = defineModel<{ name: string; age: number }>()
45
+
46
+ function updateName(newName: string) {
47
+ // CORRECT: Create new object reference
48
+ // This triggers update:modelValue event to parent
49
+ model.value = {
50
+ ...model.value,
51
+ name: newName
52
+ }
53
+ }
54
+
55
+ function addToList() {
56
+ // CORRECT: Create new array reference
57
+ model.value = {
58
+ ...model.value,
59
+ items: [...model.value.items, 'new item']
60
+ }
61
+ }
62
+ </script>
63
+ ```
64
+
65
+ ## Deep Nesting Requires Full Path Replacement
66
+
67
+ ```vue
68
+ <script setup>
69
+ const model = defineModel<{
70
+ user: {
71
+ address: {
72
+ city: string
73
+ }
74
+ }
75
+ }>()
76
+
77
+ // WRONG: Deep mutation
78
+ model.value.user.address.city = 'New York'
79
+
80
+ // CORRECT: Replace entire chain
81
+ model.value = {
82
+ ...model.value,
83
+ user: {
84
+ ...model.value.user,
85
+ address: {
86
+ ...model.value.user.address,
87
+ city: 'New York'
88
+ }
89
+ }
90
+ }
91
+
92
+ // ALTERNATIVE: Use structuredClone for complex updates
93
+ function updateCity(city: string) {
94
+ const updated = structuredClone(model.value)
95
+ updated.user.address.city = city
96
+ model.value = updated // New reference triggers event
97
+ }
98
+ </script>
99
+ ```
100
+
101
+ ## Race Condition Warning with Spread Operator
102
+
103
+ When multiple updates occur rapidly, earlier changes can be lost:
104
+
105
+ ```vue
106
+ <script setup>
107
+ const model = defineModel<{ a: string; b: string }>()
108
+
109
+ // CAUTION: Race condition if called in same tick
110
+ function updateBothWrong() {
111
+ model.value = { ...model.value, a: 'new-a' } // First update
112
+ model.value = { ...model.value, b: 'new-b' } // May use stale model.value!
113
+ }
114
+
115
+ // CORRECT: Batch updates into single assignment
116
+ function updateBothCorrect() {
117
+ model.value = {
118
+ ...model.value,
119
+ a: 'new-a',
120
+ b: 'new-b'
121
+ }
122
+ }
123
+ </script>
124
+ ```
125
+
126
+ ## Alternative: VueUse's useVModel with Deep Option
127
+
128
+ For complex objects, consider VueUse:
129
+
130
+ ```vue
131
+ <script setup>
132
+ import { useVModel } from '@vueuse/core'
133
+
134
+ const props = defineProps<{ modelValue: { name: string } }>()
135
+ const emit = defineEmits(['update:modelValue'])
136
+
137
+ // Deep tracking with passive updates
138
+ const model = useVModel(props, 'modelValue', emit, { deep: true, passive: true })
139
+
140
+ // Now direct mutations work
141
+ model.value.name = 'New Name' // Properly syncs with parent
142
+ </script>
143
+ ```
144
+
145
+ ## Reference
146
+ - [Vue.js Component v-model](https://vuejs.org/guide/components/v-model.html)
147
+ - [GitHub Discussion: defineModel with objects](https://github.com/orgs/vuejs/discussions/10538)
148
+ - [SIMPL Engineering: Vue defineModel Pitfalls](https://engineering.simpl.de/post/vue_definemodel/)
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: Use nextTick() to Wait for DOM Updates
3
+ impact: MEDIUM
4
+ impactDescription: DOM updates are batched and asynchronous - direct DOM access after state changes sees stale values
5
+ type: capability
6
+ tags: [vue3, dom, nextTick, reactivity, async]
7
+ ---
8
+
9
+ # Use nextTick() to Wait for DOM Updates
10
+
11
+ **Impact: MEDIUM** - Vue batches DOM updates asynchronously for performance. If you access the DOM immediately after changing reactive state, you'll see the old values. Use `nextTick()` to wait for the DOM to update.
12
+
13
+ When you modify reactive state, Vue doesn't update the DOM synchronously. Instead, it buffers changes and applies them in the next "tick" of the event loop. This is a performance optimization, but it can cause bugs when you need to read from or manipulate the DOM after state changes.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Use `await nextTick()` when you need to access updated DOM elements after state changes
18
+ - [ ] Use `nextTick()` when measuring DOM elements (heights, widths) after data changes
19
+ - [ ] Use `nextTick()` when focusing inputs or scrolling after content updates
20
+ - [ ] Consider if you really need DOM access - often you can work with reactive data instead
21
+
22
+ **Incorrect:**
23
+ ```javascript
24
+ import { ref } from 'vue'
25
+
26
+ const message = ref('Hello')
27
+ const messageEl = ref(null)
28
+
29
+ function updateMessage() {
30
+ message.value = 'Updated!'
31
+
32
+ // WRONG: DOM still shows "Hello" at this point
33
+ console.log(messageEl.value.textContent) // "Hello" - stale!
34
+
35
+ // WRONG: Scrolling/focusing may not work correctly
36
+ scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight
37
+ }
38
+ ```
39
+
40
+ **Correct:**
41
+ ```javascript
42
+ import { ref, nextTick } from 'vue'
43
+
44
+ const message = ref('Hello')
45
+ const messageEl = ref(null)
46
+
47
+ async function updateMessage() {
48
+ message.value = 'Updated!'
49
+
50
+ // CORRECT: Wait for DOM to update
51
+ await nextTick()
52
+
53
+ // Now the DOM is updated
54
+ console.log(messageEl.value.textContent) // "Updated!"
55
+
56
+ // Scrolling and focusing now work correctly
57
+ scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight
58
+ }
59
+
60
+ // Alternative: callback syntax
61
+ function updateWithCallback() {
62
+ message.value = 'Updated!'
63
+
64
+ nextTick(() => {
65
+ console.log(messageEl.value.textContent) // "Updated!"
66
+ })
67
+ }
68
+ ```
69
+
70
+ ```vue
71
+ <script setup>
72
+ import { ref, nextTick } from 'vue'
73
+
74
+ const items = ref([])
75
+ const listRef = ref(null)
76
+
77
+ async function addItem() {
78
+ items.value.push({ id: Date.now(), text: 'New item' })
79
+
80
+ await nextTick()
81
+
82
+ // Now we can safely scroll to the new item
83
+ listRef.value.lastElementChild?.scrollIntoView({ behavior: 'smooth' })
84
+ }
85
+ </script>
86
+ ```
87
+
88
+ ## Reference
89
+ - [Vue.js Reactivity Fundamentals - DOM Update Timing](https://vuejs.org/guide/essentials/reactivity-fundamentals.html#dom-update-timing)
90
+ - [Vue.js nextTick API](https://vuejs.org/api/general.html#nexttick)