@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,115 @@
1
+ # Async Component Error Handling
2
+
3
+ ## Rule
4
+
5
+ Always configure error handling for async components using `errorComponent` and/or `onError` callback. Without proper error handling, failed component loads can leave the UI in an undefined state with no user feedback.
6
+
7
+ ## Why This Matters
8
+
9
+ Network failures, timeouts, and server errors are common in production. Without error handling, users see blank spaces or broken UIs with no indication of what went wrong or how to recover.
10
+
11
+ ## Bad Code
12
+
13
+ ```vue
14
+ <script setup>
15
+ import { defineAsyncComponent } from 'vue'
16
+
17
+ // No error handling - fails silently
18
+ const AsyncWidget = defineAsyncComponent(() =>
19
+ import('./Widget.vue')
20
+ )
21
+ </script>
22
+ ```
23
+
24
+ ```vue
25
+ <script setup>
26
+ import { defineAsyncComponent } from 'vue'
27
+
28
+ // isLoading never becomes false on error - infinite spinner
29
+ const isLoading = ref(true)
30
+ const Widget = defineAsyncComponent({
31
+ loader: () => import('./Widget.vue').finally(() => {
32
+ isLoading.value = false // Only runs on success
33
+ })
34
+ })
35
+ </script>
36
+ ```
37
+
38
+ ## Good Code
39
+
40
+ ```vue
41
+ <script setup>
42
+ import { defineAsyncComponent } from 'vue'
43
+ import LoadingSpinner from './LoadingSpinner.vue'
44
+ import ErrorDisplay from './ErrorDisplay.vue'
45
+
46
+ const AsyncWidget = defineAsyncComponent({
47
+ loader: () => import('./Widget.vue'),
48
+ loadingComponent: LoadingSpinner,
49
+ errorComponent: ErrorDisplay,
50
+ delay: 200, // Prevent loading flicker
51
+ timeout: 10000 // Show error after 10 seconds
52
+ })
53
+ </script>
54
+ ```
55
+
56
+ ```vue
57
+ <script setup>
58
+ import { defineAsyncComponent } from 'vue'
59
+
60
+ // With retry logic using onError
61
+ const AsyncWidget = defineAsyncComponent({
62
+ loader: () => import('./Widget.vue'),
63
+ loadingComponent: LoadingSpinner,
64
+ errorComponent: ErrorDisplay,
65
+ onError(error, retry, fail, attempts) {
66
+ if (attempts <= 3) {
67
+ // Retry up to 3 times
68
+ retry()
69
+ } else {
70
+ // Give up and show error component
71
+ fail()
72
+ }
73
+ }
74
+ })
75
+ </script>
76
+ ```
77
+
78
+ ```vue
79
+ <script setup>
80
+ import { defineAsyncComponent } from 'vue'
81
+
82
+ // Fallback component pattern - catch in loader
83
+ const AsyncWidget = defineAsyncComponent(() =>
84
+ import('./Widget.vue').catch(() => import('./WidgetFallback.vue'))
85
+ )
86
+ </script>
87
+ ```
88
+
89
+ ## onError Callback Parameters
90
+
91
+ The `onError` callback receives four arguments:
92
+
93
+ | Parameter | Type | Description |
94
+ |-----------|------|-------------|
95
+ | `error` | `Error` | The error that caused the load to fail |
96
+ | `retry` | `Function` | Call to retry loading the component |
97
+ | `fail` | `Function` | Call to give up and show errorComponent |
98
+ | `attempts` | `number` | Number of load attempts so far |
99
+
100
+ ## Key Points
101
+
102
+ 1. Always provide an `errorComponent` for production applications
103
+ 2. Use `timeout` to prevent indefinite loading states
104
+ 3. Consider retry logic with `onError` for transient network issues
105
+ 4. The `delay` option (default 200ms) prevents loading flicker on fast networks
106
+ 5. Use the fallback pattern (`.catch()` in loader) when you want a seamless degradation
107
+
108
+ ## SSR Warning
109
+
110
+ Using `onError` with SSR can cause issues in some configurations, potentially leading to infinite loading. Test thoroughly in SSR environments.
111
+
112
+ ## References
113
+
114
+ - [Vue.js Async Components Documentation](https://vuejs.org/guide/components/async)
115
+ - [Handling Async Components' loading errors](https://awad.dev/blog/handling-async-component-loading-errors/)
@@ -0,0 +1,112 @@
1
+ # Async Components with keep-alive Ref Issues
2
+
3
+ ## Rule
4
+
5
+ When using `<keep-alive>`, `<component>`, and `defineAsyncComponent` together, be aware that template refs can become undefined when the component is re-activated after being deactivated.
6
+
7
+ ## Why This Matters
8
+
9
+ This is a known Vue issue where the ref binding works correctly on first activation but becomes undefined on subsequent activations. This can cause runtime errors when trying to access component methods or properties through refs.
10
+
11
+ ## Problem Scenario
12
+
13
+ ```vue
14
+ <script setup>
15
+ import { ref, defineAsyncComponent } from 'vue'
16
+
17
+ const AsyncWidget = defineAsyncComponent(() =>
18
+ import('./Widget.vue')
19
+ )
20
+
21
+ const currentComponent = ref(AsyncWidget)
22
+ const widgetRef = ref(null)
23
+
24
+ function callWidgetMethod() {
25
+ // May be undefined after component reactivation!
26
+ widgetRef.value?.doSomething()
27
+ }
28
+ </script>
29
+
30
+ <template>
31
+ <keep-alive>
32
+ <component :is="currentComponent" ref="widgetRef" />
33
+ </keep-alive>
34
+ </template>
35
+ ```
36
+
37
+ ## Workarounds
38
+
39
+ ### Option 1: Use onActivated to re-establish ref access
40
+
41
+ ```vue
42
+ <script setup>
43
+ import { ref, defineAsyncComponent, onActivated, nextTick } from 'vue'
44
+
45
+ const AsyncWidget = defineAsyncComponent(() =>
46
+ import('./Widget.vue')
47
+ )
48
+
49
+ const currentComponent = ref(AsyncWidget)
50
+ const widgetRef = ref(null)
51
+
52
+ // Use a computed or method that waits for ref to be available
53
+ async function callWidgetMethod() {
54
+ await nextTick()
55
+ if (widgetRef.value) {
56
+ widgetRef.value.doSomething()
57
+ }
58
+ }
59
+ </script>
60
+ ```
61
+
62
+ ### Option 2: Avoid mixing all three patterns
63
+
64
+ If possible, use one of these alternatives:
65
+
66
+ ```vue
67
+ <!-- Option A: Don't use keep-alive with async components -->
68
+ <template>
69
+ <component :is="currentComponent" ref="widgetRef" />
70
+ </template>
71
+
72
+ <!-- Option B: Use static component with keep-alive -->
73
+ <script setup>
74
+ import Widget from './Widget.vue' // Regular import
75
+ </script>
76
+ <template>
77
+ <keep-alive>
78
+ <component :is="Widget" ref="widgetRef" />
79
+ </keep-alive>
80
+ </template>
81
+ ```
82
+
83
+ ### Option 3: Use provide/inject instead of refs
84
+
85
+ ```vue
86
+ <!-- Parent.vue -->
87
+ <script setup>
88
+ import { provide, ref } from 'vue'
89
+
90
+ const sharedState = ref({ /* shared data */ })
91
+ provide('widgetState', sharedState)
92
+ </script>
93
+
94
+ <!-- Widget.vue (async component) -->
95
+ <script setup>
96
+ import { inject } from 'vue'
97
+ const widgetState = inject('widgetState')
98
+ </script>
99
+ ```
100
+
101
+ ## Key Points
102
+
103
+ 1. This is a known issue when combining `<keep-alive>`, `<component :is>`, and `defineAsyncComponent`
104
+ 2. Refs may become undefined after component deactivation/reactivation
105
+ 3. Use `nextTick` and null checks when accessing refs
106
+ 4. Consider alternative patterns like provide/inject for cross-component communication
107
+ 5. Test thoroughly when using this combination
108
+
109
+ ## References
110
+
111
+ - [Vue.js GitHub Discussion #11334](https://github.com/orgs/vuejs/discussions/11334)
112
+ - [Vue.js Async Components Documentation](https://vuejs.org/guide/components/async)
@@ -0,0 +1,84 @@
1
+ ---
2
+ title: Suspense Overrides Async Component Loading and Error Options
3
+ impact: MEDIUM
4
+ impactDescription: Async component loading/error options are ignored under a parent Suspense, leading to missing spinners and error UIs
5
+ type: gotcha
6
+ tags: [vue3, suspense, async-components, loading, error-handling]
7
+ ---
8
+
9
+ # Suspense Overrides Async Component Loading and Error Options
10
+
11
+ **Impact: MEDIUM** - When an async component renders inside a parent `<Suspense>`, its `loadingComponent`, `errorComponent`, `delay`, and `timeout` options do not run. The parent Suspense controls loading and error UX instead.
12
+
13
+ ## Task Checklist
14
+
15
+ - [ ] Confirm whether the async component is inside a `<Suspense>` boundary
16
+ - [ ] Use `suspensible: false` when the component must manage its own loading/error UI
17
+ - [ ] Or move loading/error UI to the parent `<Suspense>` fallback and an error boundary (`onErrorCaptured`)
18
+ - [ ] Provide a retry path for failed loads
19
+
20
+ **Incorrect:**
21
+ ```vue
22
+ <script setup>
23
+ import { defineAsyncComponent } from 'vue'
24
+
25
+ const AsyncDashboard = defineAsyncComponent({
26
+ loader: () => import('./Dashboard.vue'),
27
+ loadingComponent: LoadingSpinner,
28
+ errorComponent: ErrorDisplay,
29
+ timeout: 3000
30
+ })
31
+ </script>
32
+
33
+ <template>
34
+ <Suspense>
35
+ <AsyncDashboard />
36
+ <template #fallback>Loading...</template>
37
+ </Suspense>
38
+ </template>
39
+ ```
40
+
41
+ **Correct (component handles its own states):**
42
+ ```vue
43
+ <script setup>
44
+ import { defineAsyncComponent } from 'vue'
45
+
46
+ const AsyncDashboard = defineAsyncComponent({
47
+ loader: () => import('./Dashboard.vue'),
48
+ loadingComponent: LoadingSpinner,
49
+ errorComponent: ErrorDisplay,
50
+ timeout: 3000,
51
+ suspensible: false
52
+ })
53
+ </script>
54
+
55
+ <template>
56
+ <AsyncDashboard />
57
+ </template>
58
+ ```
59
+
60
+ **Correct (parent Suspense owns loading/error UI):**
61
+ ```vue
62
+ <script setup>
63
+ import { onErrorCaptured, ref } from 'vue'
64
+ import AsyncDashboard from './AsyncDashboard.vue'
65
+
66
+ const error = ref(null)
67
+
68
+ onErrorCaptured((err) => {
69
+ error.value = err
70
+ return false
71
+ })
72
+ </script>
73
+
74
+ <template>
75
+ <ErrorDisplay v-if="error" :error="error" />
76
+
77
+ <Suspense v-else>
78
+ <AsyncDashboard />
79
+ <template #fallback>
80
+ <LoadingSpinner />
81
+ </template>
82
+ </Suspense>
83
+ </template>
84
+ ```
@@ -0,0 +1,109 @@
1
+ # Do Not Use defineAsyncComponent with Vue Router
2
+
3
+ ## Rule
4
+
5
+ Never use `defineAsyncComponent` when configuring Vue Router route components. Vue Router has its own lazy loading mechanism using dynamic imports directly.
6
+
7
+ ## Why This Matters
8
+
9
+ Vue Router's lazy loading is specifically designed for route-level code splitting. Using `defineAsyncComponent` for routes adds unnecessary overhead and can cause unexpected behavior with navigation guards, loading states, and route transitions.
10
+
11
+ ## Bad Code
12
+
13
+ ```javascript
14
+ import { defineAsyncComponent } from 'vue'
15
+ import { createRouter, createWebHistory } from 'vue-router'
16
+
17
+ const router = createRouter({
18
+ history: createWebHistory(),
19
+ routes: [
20
+ {
21
+ path: '/dashboard',
22
+ // WRONG: Don't use defineAsyncComponent here
23
+ component: defineAsyncComponent(() =>
24
+ import('./views/Dashboard.vue')
25
+ )
26
+ },
27
+ {
28
+ path: '/profile',
29
+ // WRONG: This also won't work as expected
30
+ component: defineAsyncComponent({
31
+ loader: () => import('./views/Profile.vue'),
32
+ loadingComponent: LoadingSpinner
33
+ })
34
+ }
35
+ ]
36
+ })
37
+ ```
38
+
39
+ ## Good Code
40
+
41
+ ```javascript
42
+ import { createRouter, createWebHistory } from 'vue-router'
43
+
44
+ const router = createRouter({
45
+ history: createWebHistory(),
46
+ routes: [
47
+ {
48
+ path: '/dashboard',
49
+ // CORRECT: Use dynamic import directly
50
+ component: () => import('./views/Dashboard.vue')
51
+ },
52
+ {
53
+ path: '/profile',
54
+ // CORRECT: Simple arrow function with import
55
+ component: () => import('./views/Profile.vue')
56
+ }
57
+ ]
58
+ })
59
+ ```
60
+
61
+ ## Handling Loading States with Vue Router
62
+
63
+ For route-level loading states, use Vue Router's navigation guards or a global loading indicator:
64
+
65
+ ```vue
66
+ <script setup>
67
+ import { ref } from 'vue'
68
+ import { useRouter } from 'vue-router'
69
+
70
+ const router = useRouter()
71
+ const isLoading = ref(false)
72
+
73
+ router.beforeEach(() => {
74
+ isLoading.value = true
75
+ })
76
+
77
+ router.afterEach(() => {
78
+ isLoading.value = false
79
+ })
80
+ </script>
81
+
82
+ <template>
83
+ <LoadingBar v-if="isLoading" />
84
+ <RouterView />
85
+ </template>
86
+ ```
87
+
88
+ ## When to Use defineAsyncComponent
89
+
90
+ Use `defineAsyncComponent` for:
91
+ - Components loaded conditionally within a page
92
+ - Heavy components that aren't always needed
93
+ - Modal dialogs or panels that load on demand
94
+
95
+ Use Vue Router's lazy loading for:
96
+ - Route-level components (views/pages)
97
+ - Any component configured in route definitions
98
+
99
+ ## Key Points
100
+
101
+ 1. Vue Router and `defineAsyncComponent` are separate lazy loading mechanisms
102
+ 2. Route components should use direct dynamic imports: `() => import('./View.vue')`
103
+ 3. Use navigation guards for route-level loading indicators
104
+ 4. `defineAsyncComponent` is for component-level lazy loading within pages
105
+
106
+ ## References
107
+
108
+ - [Vue Router Lazy Loading Routes](https://router.vuejs.org/guide/advanced/lazy-loading.html)
109
+ - [Vue.js Async Components Documentation](https://vuejs.org/guide/components/async)
@@ -0,0 +1,205 @@
1
+ # Fallthrough Event Listeners Are Additive
2
+
3
+ ## Rule
4
+
5
+ When an event listener is passed to a component as a fallthrough attribute, it is added to the root element's existing listeners of the same type - both will trigger. This is different from props where values are replaced. Be aware that both the component's internal handler and the parent's handler will execute.
6
+
7
+ ## Why This Matters
8
+
9
+ - Developers may expect event listeners to override like props
10
+ - Both handlers execute, which can cause double submissions, duplicate API calls
11
+ - Order of execution: internal handler first, then fallthrough handler
12
+ - This is actually useful for composition but can cause bugs if unexpected
13
+
14
+ ## Bad Code
15
+
16
+ ```vue
17
+ <!-- BaseButton.vue - Potential double-action bug -->
18
+ <template>
19
+ <button @click="internalClick">
20
+ <slot />
21
+ </button>
22
+ </template>
23
+
24
+ <script setup>
25
+ const emit = defineEmits(['action'])
26
+
27
+ function internalClick() {
28
+ // This runs first
29
+ emit('action')
30
+ console.log('Internal click handler')
31
+ }
32
+ </script>
33
+
34
+ <!-- Parent.vue -->
35
+ <template>
36
+ <BaseButton @click="parentClick">Submit</BaseButton>
37
+ </template>
38
+
39
+ <script setup>
40
+ function parentClick() {
41
+ // This ALSO runs (after internal)
42
+ submitForm() // Might cause double submission!
43
+ console.log('Parent click handler')
44
+ }
45
+ </script>
46
+
47
+ <!--
48
+ RESULT: Both handlers fire!
49
+ Console output:
50
+ 1. "Internal click handler"
51
+ 2. "Parent click handler"
52
+
53
+ If both trigger API calls, you get duplicate requests
54
+ -->
55
+ ```
56
+
57
+ ## Good Code
58
+
59
+ ### Option 1: Prevent fallthrough with inheritAttrs: false
60
+
61
+ ```vue
62
+ <!-- BaseButton.vue - Control event handling explicitly -->
63
+ <script setup>
64
+ defineOptions({
65
+ inheritAttrs: false
66
+ })
67
+
68
+ const emit = defineEmits(['click'])
69
+
70
+ function handleClick(event) {
71
+ // Component controls all click behavior
72
+ console.log('Handled internally')
73
+ emit('click', event) // Explicitly forward if needed
74
+ }
75
+ </script>
76
+
77
+ <template>
78
+ <button @click="handleClick">
79
+ <slot />
80
+ </button>
81
+ </template>
82
+ ```
83
+
84
+ ### Option 2: Document the additive behavior
85
+
86
+ ```vue
87
+ <!-- BaseButton.vue - Design for composition -->
88
+ <script setup>
89
+ /**
90
+ * BaseButton - A composable button component
91
+ *
92
+ * Note: Click handlers passed to this component are ADDITIVE.
93
+ * The internal handler runs first, then any parent @click handler.
94
+ * Use @action event if you only want to respond to the action.
95
+ */
96
+ const emit = defineEmits(['action'])
97
+
98
+ function internalClick() {
99
+ // Internal logic (e.g., ripple effect, analytics)
100
+ emit('action')
101
+ }
102
+ </script>
103
+
104
+ <template>
105
+ <button @click="internalClick">
106
+ <slot />
107
+ </button>
108
+ </template>
109
+
110
+ <!-- Parent.vue - Use the custom event instead -->
111
+ <template>
112
+ <!-- Use @action, not @click, to avoid double handling -->
113
+ <BaseButton @action="handleAction">Submit</BaseButton>
114
+ </template>
115
+ ```
116
+
117
+ ### Option 3: Use stopPropagation if needed
118
+
119
+ ```vue
120
+ <!-- BaseButton.vue - Stop event propagation when needed -->
121
+ <script setup>
122
+ const props = defineProps({
123
+ stopPropagation: Boolean
124
+ })
125
+
126
+ function handleClick(event) {
127
+ if (props.stopPropagation) {
128
+ event.stopPropagation()
129
+ }
130
+ // Internal handling...
131
+ }
132
+ </script>
133
+
134
+ <template>
135
+ <button @click="handleClick">
136
+ <slot />
137
+ </button>
138
+ </template>
139
+ ```
140
+
141
+ ## Using Additive Behavior Intentionally
142
+
143
+ The additive behavior can be useful for extending functionality:
144
+
145
+ ```vue
146
+ <!-- EnhancedButton.vue - Leveraging additive listeners -->
147
+ <template>
148
+ <button
149
+ @click="trackClick"
150
+ @focus="trackFocus"
151
+ >
152
+ <slot />
153
+ </button>
154
+ </template>
155
+
156
+ <script setup>
157
+ function trackClick() {
158
+ analytics.track('button_click')
159
+ // Parent's @click will also run - that's intentional!
160
+ }
161
+
162
+ function trackFocus() {
163
+ analytics.track('button_focus')
164
+ }
165
+ </script>
166
+
167
+ <!-- Parent.vue -->
168
+ <template>
169
+ <!-- Both analytics AND form submission happen -->
170
+ <EnhancedButton @click="submitForm">Submit</EnhancedButton>
171
+ </template>
172
+ ```
173
+
174
+ ## Execution Order
175
+
176
+ ```vue
177
+ <script setup>
178
+ // Component
179
+ function componentHandler() {
180
+ console.log('1. Component handler (first)')
181
+ }
182
+ </script>
183
+
184
+ <template>
185
+ <button @click="componentHandler">Click</button>
186
+ </template>
187
+
188
+ <!-- Parent passes @click -->
189
+ <!-- Execution order:
190
+ 1. componentHandler (defined in component)
191
+ 2. parentHandler (passed as fallthrough)
192
+ -->
193
+ ```
194
+
195
+ ## Best Practices
196
+
197
+ 1. **For UI components**: Use `inheritAttrs: false` and emit custom events
198
+ 2. **For HOCs/wrappers**: Document that listeners are additive
199
+ 3. **For analytics/tracking**: Leverage additive behavior intentionally
200
+ 4. **Avoid side effects**: Don't assume your handler is the only one running
201
+
202
+ ## References
203
+
204
+ - [Fallthrough Attributes - v-on Listener Inheritance](https://vuejs.org/guide/components/attrs.html#v-on-listener-inheritance)
205
+ - [Component Events](https://vuejs.org/guide/components/events.html)