@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,350 @@
1
+ ---
2
+ title: Component Data Flow Best Practices
3
+ impact: HIGH
4
+ impactDescription: Clear data flow between components prevents state bugs, stale UI, and brittle coupling
5
+ type: best-practice
6
+ tags: [vue3, props, emits, v-model, provide-inject, data-flow, typescript]
7
+ ---
8
+
9
+ # Component Data Flow Best Practices
10
+
11
+ **Impact: HIGH** - Vue components stay reliable when data flow is explicit: props go down, events go up, `v-model` handles two-way bindings, and provide/inject supports cross-tree dependencies. Blurring these boundaries leads to stale state, hidden coupling, and hard-to-debug UI.
12
+
13
+ The main principle of data flow in Vue.js is **Props Down / Events Up**. This is the most maintainable default, and one-way flow scales well.
14
+
15
+ ## Task List
16
+
17
+ - Treat props as read-only inputs
18
+ - Prefer reactive props destructure for defaults in modern Vue (3.5+), and watch destructured props with a getter
19
+ - Use props/emit for component communication; reserve refs for imperative actions
20
+ - When refs are required for imperative APIs, type them with template refs
21
+ - Emit events instead of mutating parent state directly
22
+ - Use `defineModel` for v-model in modern Vue (3.4+)
23
+ - Handle v-model modifiers deliberately in child components
24
+ - Use symbols for provide/inject keys to avoid props drilling (over ~3 layers)
25
+ - Keep mutations in the provider or expose explicit actions
26
+ - In TypeScript projects, prefer type-based `defineProps`, `defineEmits`, and `InjectionKey`
27
+
28
+ ## Props: One-Way Data Down
29
+
30
+ Props are inputs. Do not mutate them in the child.
31
+
32
+ **BAD:**
33
+ ```vue
34
+ <script setup>
35
+ const props = defineProps({ count: Number })
36
+
37
+ function increment() {
38
+ props.count++
39
+ }
40
+ </script>
41
+ ```
42
+
43
+ **GOOD:**
44
+
45
+ If state needs to change, emit an event, use `v-model` or create a local copy.
46
+
47
+ ## Reactive Props Destructure (Vue 3.5+)
48
+
49
+ Since Vue 3.5, variables destructured from `defineProps` stay reactive. Prefer this for declaring defaults: use native JS default syntax instead of `withDefaults`. Destructured props remain read-only.
50
+
51
+ **BAD (verbose `withDefaults`):**
52
+ ```vue
53
+ <script setup lang="ts">
54
+ interface Props {
55
+ msg?: string
56
+ labels?: string[]
57
+ }
58
+
59
+ const props = withDefaults(defineProps<Props>(), {
60
+ msg: 'hello',
61
+ labels: () => ['one', 'two']
62
+ })
63
+ </script>
64
+ ```
65
+
66
+ **GOOD (Vue 3.5+):**
67
+ ```vue
68
+ <script setup lang="ts">
69
+ interface Props {
70
+ msg?: string
71
+ labels?: string[]
72
+ }
73
+
74
+ // Each instance gets its own copy of mutable defaults — no factory needed
75
+ const { msg = 'hello', labels = ['one', 'two'] } = defineProps<Props>()
76
+ </script>
77
+ ```
78
+
79
+ **Gotcha — watching or passing a destructured prop needs a getter.** A destructured prop is a plain variable, so passing it directly to `watch` (or any function expecting a reactive source) loses reactivity; Vue's compiler warns on this. Wrap it in a getter:
80
+
81
+ ```ts
82
+ // ❌ not reactive — passes a value, not a source
83
+ watch(msg, () => { /* ... */ })
84
+
85
+ // ✅ getter preserves reactivity
86
+ watch(() => msg, () => { /* ... */ })
87
+ ```
88
+
89
+ ## Prefer props/emit over component refs
90
+
91
+ **BAD:**
92
+ ```vue
93
+ <script setup>
94
+ import { ref } from 'vue'
95
+ import UserForm from './UserForm.vue'
96
+
97
+ const formRef = ref(null)
98
+
99
+ function submitForm() {
100
+ if (formRef.value.isValid) {
101
+ formRef.value.submit()
102
+ }
103
+ }
104
+ </script>
105
+
106
+ <template>
107
+ <UserForm ref="formRef" />
108
+ <button @click="submitForm">Submit</button>
109
+ </template>
110
+ ```
111
+
112
+ **GOOD:**
113
+ ```vue
114
+ <script setup>
115
+ import UserForm from './UserForm.vue'
116
+
117
+ function handleSubmit(formData) {
118
+ api.submit(formData)
119
+ }
120
+ </script>
121
+
122
+ <template>
123
+ <UserForm @submit="handleSubmit" />
124
+ </template>
125
+ ```
126
+
127
+ ## Type component refs when imperative access is required
128
+
129
+ Prefer props/emits by default. When a parent must call an exposed child method, type the ref explicitly and expose only the intended API from the child with `defineExpose`.
130
+
131
+ **BAD:**
132
+ ```vue
133
+ <script setup lang="ts">
134
+ import { ref, onMounted } from 'vue'
135
+ import DialogPanel from './DialogPanel.vue'
136
+
137
+ const panelRef = ref(null)
138
+
139
+ onMounted(() => {
140
+ panelRef.value.open()
141
+ })
142
+ </script>
143
+
144
+ <template>
145
+ <DialogPanel ref="panelRef" />
146
+ </template>
147
+ ```
148
+
149
+ **GOOD:**
150
+ ```vue
151
+ <!-- DialogPanel.vue -->
152
+ <script setup lang="ts">
153
+ function open() {}
154
+
155
+ defineExpose({ open })
156
+ </script>
157
+ ```
158
+
159
+ ```vue
160
+ <!-- Parent.vue -->
161
+ <script setup lang="ts">
162
+ import { onMounted, useTemplateRef } from 'vue'
163
+ import DialogPanel from './DialogPanel.vue'
164
+
165
+ // Vue 3.5+ with useTemplateRef
166
+ const panelRef = useTemplateRef('panelRef')
167
+
168
+ // Before Vue 3.5 with manual typing and ref
169
+ // const panelRef = ref<InstanceType<typeof DialogPanel> | null>(null)
170
+
171
+ onMounted(() => {
172
+ panelRef.value?.open()
173
+ })
174
+ </script>
175
+
176
+ <template>
177
+ <DialogPanel ref="panelRef" />
178
+ </template>
179
+ ```
180
+
181
+ ## Emits: Explicit Events Up
182
+
183
+ Component events do not bubble. If a parent needs to know about an event, re-emit it explicitly.
184
+
185
+ **BAD:**
186
+ ```vue
187
+ <!-- Parent expects "saved" from grandchild, but it won't bubble -->
188
+ <Child @saved="onSaved" />
189
+ ```
190
+
191
+ **GOOD:**
192
+ ```vue
193
+ <!-- Child.vue -->
194
+ <script setup>
195
+ const emit = defineEmits(['saved'])
196
+
197
+ function onGrandchildSaved(payload) {
198
+ emit('saved', payload)
199
+ }
200
+ </script>
201
+
202
+ <template>
203
+ <Grandchild @saved="onGrandchildSaved" />
204
+ </template>
205
+ ```
206
+
207
+ **Event naming:** use kebab-case in templates and camelCase in script:
208
+ ```vue
209
+ <script setup>
210
+ const emit = defineEmits(['updateUser'])
211
+ </script>
212
+
213
+ <template>
214
+ <ProfileForm @update-user="emit('updateUser', $event)" />
215
+ </template>
216
+ ```
217
+
218
+ ## `v-model`: Predictable Two-Way Bindings
219
+
220
+ Use `defineModel` by default for component bindings and emit updates on input. Only use the `modelValue` + `update:modelValue` pattern if you are on Vue < 3.4.
221
+
222
+ **BAD:**
223
+ ```vue
224
+ <script setup>
225
+ const props = defineProps({ value: String })
226
+ </script>
227
+
228
+ <template>
229
+ <input :value="props.value" @input="$emit('input', $event.target.value)" />
230
+ </template>
231
+ ```
232
+
233
+ **GOOD (Vue 3.4+):**
234
+ ```vue
235
+ <script setup>
236
+ const model = defineModel({ type: String })
237
+ </script>
238
+
239
+ <template>
240
+ <input v-model="model" />
241
+ </template>
242
+ ```
243
+
244
+ **GOOD (Vue < 3.4):**
245
+ ```vue
246
+ <script setup>
247
+ const props = defineProps({ modelValue: String })
248
+ const emit = defineEmits(['update:modelValue'])
249
+ </script>
250
+
251
+ <template>
252
+ <input
253
+ :value="props.modelValue"
254
+ @input="emit('update:modelValue', $event.target.value)"
255
+ />
256
+ </template>
257
+ ```
258
+
259
+ If you need the updated value immediately after a change, use the input event value or `nextTick` in the parent.
260
+
261
+ ## Provide/Inject: Shared Context Without Prop Drilling
262
+
263
+ Use provide/inject for cross-tree state, but keep mutations centralized in the provider and expose explicit actions.
264
+
265
+ **BAD:**
266
+ ```vue
267
+ // Provider.vue
268
+ provide('theme', reactive({ dark: false }))
269
+
270
+ // Consumer.vue
271
+ const theme = inject('theme')
272
+ // Mutating shared state from any depth becomes hard to track
273
+ theme.dark = true
274
+ ```
275
+
276
+ **GOOD:**
277
+ ```vue
278
+ // Provider.vue
279
+ const theme = reactive({ dark: false })
280
+ const toggleTheme = () => { theme.dark = !theme.dark }
281
+
282
+ provide(themeKey, readonly(theme))
283
+ provide(themeActionsKey, { toggleTheme })
284
+
285
+ // Consumer.vue
286
+ const theme = inject(themeKey)
287
+ const { toggleTheme } = inject(themeActionsKey)
288
+ ```
289
+
290
+ Use symbols for keys to avoid collisions in large apps:
291
+ ```ts
292
+ export const themeKey = Symbol('theme')
293
+ export const themeActionsKey = Symbol('theme-actions')
294
+ ```
295
+
296
+ ## Use TypeScript Contracts for Public Component APIs
297
+
298
+ In TypeScript projects, type component boundaries directly with `defineProps`, `defineEmits`, and `InjectionKey` so invalid payloads and mismatched injections fail at compile time.
299
+
300
+ **BAD:**
301
+ ```vue
302
+ <script setup lang="ts">
303
+ import { inject } from 'vue'
304
+
305
+ const props = defineProps({
306
+ userId: String
307
+ })
308
+
309
+ const emit = defineEmits(['save'])
310
+ const settings = inject('settings')
311
+
312
+ // Payload shape is not checked here
313
+ emit('save', 123)
314
+
315
+ // Key is string-based and not type-safe
316
+ settings?.theme = 'dark'
317
+ </script>
318
+ ```
319
+
320
+ **GOOD:**
321
+ ```vue
322
+ <script setup lang="ts">
323
+ import { inject, provide } from 'vue'
324
+ import type { InjectionKey } from 'vue'
325
+
326
+ interface Props {
327
+ userId: string
328
+ }
329
+
330
+ interface Emits {
331
+ save: [payload: { id: string; draft: boolean }]
332
+ }
333
+
334
+ interface Settings {
335
+ theme: 'light' | 'dark'
336
+ }
337
+
338
+ const settingsKey: InjectionKey<Settings> = Symbol('settings')
339
+
340
+ const props = defineProps<Props>()
341
+ const emit = defineEmits<Emits>()
342
+
343
+ provide(settingsKey, { theme: 'light' })
344
+
345
+ const settings = inject(settingsKey)
346
+ if (settings) {
347
+ emit('save', { id: props.userId, draft: false })
348
+ }
349
+ </script>
350
+ ```
@@ -0,0 +1,174 @@
1
+ ---
2
+ title: Component Fallthrough Attributes Best Practices
3
+ impact: MEDIUM
4
+ impactDescription: Incorrect $attrs access and reactivity assumptions can cause undefined values and watchers that never run
5
+ type: best-practice
6
+ tags: [vue3, attrs, fallthrough-attributes, composition-api, reactivity]
7
+ ---
8
+
9
+ # Component Fallthrough Attributes Best Practices
10
+
11
+ **Impact: MEDIUM** - Fallthrough attributes are straightforward once you follow Vue's conventions: hyphenated names use bracket notation, listener keys are camelCase `onX`, and `useAttrs()` is current-but-not-reactive.
12
+
13
+ ## Task List
14
+
15
+ - Access hyphenated attribute names with bracket notation (for example `attrs['data-testid']`)
16
+ - Access event listeners with camelCase `onX` keys (for example `attrs.onClick`)
17
+ - Do not `watch()` values returned from `useAttrs()`; those watchers do not trigger on attr changes
18
+ - Use `onUpdated()` for attr-driven side effects
19
+ - Promote frequently observed attrs to props when reactive observation is required
20
+
21
+ ## Access Attribute and Listener Keys Correctly
22
+
23
+ Hyphenated attribute names preserve their original casing in JavaScript, so dot notation does not work for keys that include `-`.
24
+
25
+ **BAD:**
26
+ ```vue
27
+ <script setup>
28
+ import { useAttrs } from 'vue'
29
+
30
+ const attrs = useAttrs()
31
+
32
+ console.log(attrs.data-testid) // Syntax error
33
+ console.log(attrs.dataTestid) // undefined for data-testid
34
+ console.log(attrs['on-click']) // undefined
35
+ console.log(attrs['@click']) // undefined
36
+ </script>
37
+ ```
38
+
39
+ **GOOD:**
40
+ ```vue
41
+ <script setup>
42
+ import { useAttrs } from 'vue'
43
+
44
+ const attrs = useAttrs()
45
+
46
+ console.log(attrs['data-testid'])
47
+ console.log(attrs['aria-label'])
48
+ console.log(attrs['foo-bar'])
49
+
50
+ console.log(attrs.onClick)
51
+ console.log(attrs.onCustomEvent)
52
+ console.log(attrs.onMouseEnter)
53
+ </script>
54
+ ```
55
+
56
+ ### Naming Reference
57
+
58
+ | Parent Usage | Access in `attrs` |
59
+ |--------------|-------------------|
60
+ | `class="foo"` | `attrs.class` |
61
+ | `data-id="123"` | `attrs['data-id']` |
62
+ | `aria-label="..."` | `attrs['aria-label']` |
63
+ | `foo-bar="baz"` | `attrs['foo-bar']` |
64
+ | `@click="fn"` | `attrs.onClick` |
65
+ | `@custom-event="fn"` | `attrs.onCustomEvent` |
66
+ | `@update:modelValue="fn"` | `attrs['onUpdate:modelValue']` |
67
+
68
+ ## `useAttrs()` Is Not Reactive
69
+
70
+ `useAttrs()` always reflects the latest values, but it is intentionally not reactive for watcher tracking.
71
+
72
+ **BAD:**
73
+ ```vue
74
+ <script setup>
75
+ import { watch, watchEffect, useAttrs } from 'vue'
76
+
77
+ const attrs = useAttrs()
78
+
79
+ watch(
80
+ () => attrs.someAttr,
81
+ (newValue) => {
82
+ console.log('Changed:', newValue) // Never runs on attr changes
83
+ }
84
+ )
85
+
86
+ watchEffect(() => {
87
+ console.log(attrs.class) // Runs on setup, not on attr updates
88
+ })
89
+ </script>
90
+ ```
91
+
92
+ **GOOD:**
93
+ ```vue
94
+ <script setup>
95
+ import { onUpdated, useAttrs } from 'vue'
96
+
97
+ const attrs = useAttrs()
98
+
99
+ onUpdated(() => {
100
+ console.log('Latest attrs:', attrs)
101
+ })
102
+ </script>
103
+ ```
104
+
105
+ **GOOD:**
106
+ ```vue
107
+ <script setup>
108
+ import { watch } from 'vue'
109
+
110
+ const props = defineProps({
111
+ someAttr: String
112
+ })
113
+
114
+ watch(
115
+ () => props.someAttr,
116
+ (newValue) => {
117
+ console.log('Changed:', newValue)
118
+ }
119
+ )
120
+ </script>
121
+ ```
122
+
123
+ ## Common Patterns
124
+
125
+ ### Check for optional attrs safely
126
+
127
+ ```vue
128
+ <script setup>
129
+ import { computed, useAttrs } from 'vue'
130
+
131
+ const attrs = useAttrs()
132
+
133
+ const hasTestId = computed(() => 'data-testid' in attrs)
134
+ const ariaLabel = computed(() => attrs['aria-label'] ?? 'Default label')
135
+ </script>
136
+ ```
137
+
138
+ ### Forward listeners after internal logic
139
+
140
+ ```vue
141
+ <script setup>
142
+ import { useAttrs } from 'vue'
143
+
144
+ defineOptions({ inheritAttrs: false })
145
+
146
+ const attrs = useAttrs()
147
+
148
+ function handleClick(event) {
149
+ console.log('Internal handling first')
150
+ attrs.onClick?.(event)
151
+ }
152
+ </script>
153
+
154
+ <template>
155
+ <button @click="handleClick">
156
+ <slot />
157
+ </button>
158
+ </template>
159
+ ```
160
+
161
+ ## TypeScript Notes
162
+
163
+ `useAttrs()` is typed as `Record<string, unknown>`, so cast individual keys when needed.
164
+
165
+ ```vue
166
+ <script setup lang="ts">
167
+ import { useAttrs } from 'vue'
168
+
169
+ const attrs = useAttrs()
170
+
171
+ const testId = attrs['data-testid'] as string | undefined
172
+ const onClick = attrs.onClick as ((event: MouseEvent) => void) | undefined
173
+ </script>
174
+ ```
@@ -0,0 +1,137 @@
1
+ ---
2
+ title: KeepAlive Component Best Practices
3
+ impact: HIGH
4
+ impactDescription: KeepAlive caches component instances; misuse causes stale data, memory growth, or unexpected lifecycle behavior
5
+ type: best-practice
6
+ tags: [vue3, keepalive, cache, performance, router, dynamic-components]
7
+ ---
8
+
9
+ # KeepAlive Component Best Practices
10
+
11
+ **Impact: HIGH** - `<KeepAlive>` caches component instances instead of destroying them. Use it to preserve state across switches, but manage cache size and freshness explicitly to avoid memory growth or stale UI.
12
+
13
+ ## Task List
14
+
15
+ - Use KeepAlive only where state preservation improves UX
16
+ - Set a reasonable `max` to cap cache size
17
+ - Declare component names for include/exclude matching
18
+ - Use `onActivated`/`onDeactivated` for cache-aware logic
19
+ - Decide how and when cached views refresh their data
20
+ - Avoid caching memory-heavy or security-sensitive views
21
+
22
+ ## When to Use KeepAlive
23
+
24
+ Use KeepAlive when switching between views where state should persist (tabs, multi-step forms, dashboards). Avoid it when each visit should start fresh.
25
+
26
+ **BAD:**
27
+ ```vue
28
+ <template>
29
+ <!-- State resets on every switch -->
30
+ <component :is="currentTab" />
31
+ </template>
32
+ ```
33
+
34
+ **GOOD:**
35
+ ```vue
36
+ <template>
37
+ <!-- State preserved between switches -->
38
+ <KeepAlive>
39
+ <component :is="currentTab" />
40
+ </KeepAlive>
41
+ </template>
42
+ ```
43
+
44
+ ## When NOT to Use KeepAlive
45
+
46
+ - Search or filter pages where users expect fresh results
47
+ - Memory-heavy components (maps, large tables, media players)
48
+ - Sensitive flows where data must be cleared on exit
49
+ - Components with heavy background activity you cannot pause
50
+
51
+ ## Limit and Control the Cache
52
+
53
+ Always cap cache size with `max` and restrict caching to specific components when possible.
54
+
55
+ ```vue
56
+ <template>
57
+ <KeepAlive :max="5" include="Dashboard,Settings">
58
+ <component :is="currentView" />
59
+ </KeepAlive>
60
+ </template>
61
+ ```
62
+
63
+ ## Ensure Component Names Match include/exclude
64
+
65
+ `include` and `exclude` match the component `name` option. Explicitly set names for reliable caching.
66
+
67
+ ```vue
68
+ <!-- TabA.vue -->
69
+ <script setup>
70
+ defineOptions({ name: 'TabA' })
71
+ </script>
72
+ ```
73
+
74
+ ```vue
75
+ <template>
76
+ <KeepAlive include="TabA,TabB">
77
+ <component :is="currentTab" />
78
+ </KeepAlive>
79
+ </template>
80
+ ```
81
+
82
+ ## Cache Invalidation Strategies
83
+
84
+ Vue 3 has no direct API to remove a specific cached instance. Use keys or dynamic include/exclude to force refreshes.
85
+
86
+ ```vue
87
+ <script setup>
88
+ import { ref, reactive } from 'vue'
89
+
90
+ const currentView = ref('Dashboard')
91
+ const viewKeys = reactive({ Dashboard: 0, Settings: 0 })
92
+
93
+ function invalidateCache(view) {
94
+ viewKeys[view]++
95
+ }
96
+ </script>
97
+
98
+ <template>
99
+ <KeepAlive>
100
+ <component :is="currentView" :key="`${currentView}-${viewKeys[currentView]}`" />
101
+ </KeepAlive>
102
+ </template>
103
+ ```
104
+
105
+ ## Lifecycle Hooks for Cached Components
106
+
107
+ Cached components are not destroyed on switch. Use activation hooks for refresh and cleanup.
108
+
109
+ ```vue
110
+ <script setup>
111
+ import { onActivated, onDeactivated } from 'vue'
112
+
113
+ onActivated(() => {
114
+ refreshData()
115
+ })
116
+
117
+ onDeactivated(() => {
118
+ pauseTimers()
119
+ })
120
+ </script>
121
+ ```
122
+
123
+ ## Router Caching and Freshness
124
+
125
+ Decide whether navigation should show cached state or a fresh view. A common pattern is to key by route when params change.
126
+
127
+ ```vue
128
+ <template>
129
+ <router-view v-slot="{ Component, route }">
130
+ <KeepAlive>
131
+ <component :is="Component" :key="route.fullPath" />
132
+ </KeepAlive>
133
+ </router-view>
134
+ </template>
135
+ ```
136
+
137
+ If you want cache reuse but fresh data, refresh in `onActivated` and compare query/params before fetching.