@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,235 @@
1
+ ---
2
+ title: Provide Must Be Called Synchronously During Setup
3
+ impact: HIGH
4
+ impactDescription: Calling provide() asynchronously or conditionally may fail silently or cause inconsistent injection behavior
5
+ type: gotcha
6
+ tags: [vue3, provide-inject, composition-api, async, setup]
7
+ ---
8
+
9
+ # Provide Must Be Called Synchronously During Setup
10
+
11
+ **Impact: HIGH** - The `provide()` function must be called synchronously during the component's `setup()` phase. Calling it asynchronously (inside callbacks, promises, or after await) will fail silently, and descendant components will not receive the provided value.
12
+
13
+ ## Task Checklist
14
+
15
+ - [ ] Always call `provide()` at the top level of `setup()` or `<script setup>`
16
+ - [ ] Never call `provide()` inside async callbacks or after await statements
17
+ - [ ] For async data, provide a ref first, then update its value later
18
+ - [ ] Use immediate `provide()` with reactive containers for dynamic data
19
+
20
+ ## The Gotcha: Async Provide Fails Silently
21
+
22
+ **Wrong - Provide after async operation:**
23
+ ```vue
24
+ <script setup>
25
+ import { provide } from 'vue'
26
+
27
+ // WRONG: provide() called after await - will NOT work
28
+ onMounted(async () => {
29
+ const userData = await fetchUser()
30
+ provide('user', userData) // Silent failure!
31
+ })
32
+ </script>
33
+ ```
34
+
35
+ **Wrong - Provide inside callback:**
36
+ ```vue
37
+ <script setup>
38
+ import { provide } from 'vue'
39
+
40
+ // WRONG: provide() inside callback - will NOT work
41
+ setTimeout(() => {
42
+ provide('config', { theme: 'dark' }) // Silent failure!
43
+ }, 0)
44
+ </script>
45
+ ```
46
+
47
+ **Wrong - Provide after await in setup:**
48
+ ```vue
49
+ <script setup>
50
+ import { provide } from 'vue'
51
+
52
+ const response = await fetch('/api/config')
53
+ const config = await response.json()
54
+
55
+ // WRONG: This is after an await, setup context may be lost
56
+ provide('config', config) // May not work reliably
57
+ </script>
58
+ ```
59
+
60
+ ## Solution: Provide Synchronously, Update Async
61
+
62
+ **Correct - Provide ref immediately, update later:**
63
+ ```vue
64
+ <script setup>
65
+ import { provide, ref, onMounted } from 'vue'
66
+
67
+ // Provide immediately with initial value
68
+ const user = ref(null)
69
+ const isLoading = ref(true)
70
+ const error = ref(null)
71
+
72
+ provide('userState', {
73
+ user,
74
+ isLoading,
75
+ error
76
+ })
77
+
78
+ // Update the ref values asynchronously
79
+ onMounted(async () => {
80
+ try {
81
+ const userData = await fetchUser()
82
+ user.value = userData
83
+ } catch (e) {
84
+ error.value = e
85
+ } finally {
86
+ isLoading.value = false
87
+ }
88
+ })
89
+ </script>
90
+ ```
91
+
92
+ ```vue
93
+ <!-- Consumer component -->
94
+ <script setup>
95
+ import { inject } from 'vue'
96
+
97
+ const { user, isLoading, error } = inject('userState')
98
+ </script>
99
+
100
+ <template>
101
+ <div v-if="isLoading">Loading...</div>
102
+ <div v-else-if="error">Error: {{ error.message }}</div>
103
+ <div v-else>Welcome, {{ user?.name }}</div>
104
+ </template>
105
+ ```
106
+
107
+ ## Pattern: Async Data Provider
108
+
109
+ Create a reusable pattern for async-provided data:
110
+
111
+ ```vue
112
+ <!-- AsyncDataProvider.vue -->
113
+ <script setup>
114
+ import { provide, ref, onMounted, watch } from 'vue'
115
+
116
+ const props = defineProps({
117
+ fetchFn: {
118
+ type: Function,
119
+ required: true
120
+ },
121
+ provideKey: {
122
+ type: [String, Symbol],
123
+ required: true
124
+ },
125
+ immediate: {
126
+ type: Boolean,
127
+ default: true
128
+ }
129
+ })
130
+
131
+ const data = ref(null)
132
+ const isLoading = ref(false)
133
+ const error = ref(null)
134
+
135
+ async function load() {
136
+ isLoading.value = true
137
+ error.value = null
138
+
139
+ try {
140
+ data.value = await props.fetchFn()
141
+ } catch (e) {
142
+ error.value = e
143
+ } finally {
144
+ isLoading.value = false
145
+ }
146
+ }
147
+
148
+ // Provide synchronously
149
+ provide(props.provideKey, {
150
+ data,
151
+ isLoading,
152
+ error,
153
+ reload: load
154
+ })
155
+
156
+ // Fetch asynchronously
157
+ if (props.immediate) {
158
+ onMounted(load)
159
+ }
160
+ </script>
161
+
162
+ <template>
163
+ <slot />
164
+ </template>
165
+ ```
166
+
167
+ Usage:
168
+
169
+ ```vue
170
+ <template>
171
+ <AsyncDataProvider
172
+ :fetch-fn="() => api.getUser(userId)"
173
+ provide-key="userData"
174
+ >
175
+ <UserProfile />
176
+ </AsyncDataProvider>
177
+ </template>
178
+ ```
179
+
180
+ ## Why This Happens
181
+
182
+ Vue's `provide()` relies on the current component instance context, which is only available synchronously during setup. After setup completes:
183
+
184
+ 1. The setup context is cleared
185
+ 2. `provide()` can't find the current instance
186
+ 3. The call fails silently (no error thrown)
187
+
188
+ ## Checking for Setup Context
189
+
190
+ You can verify if setup context is available:
191
+
192
+ ```js
193
+ import { getCurrentInstance } from 'vue'
194
+
195
+ function debugProvide(key, value) {
196
+ const instance = getCurrentInstance()
197
+
198
+ if (!instance) {
199
+ console.error(
200
+ `provide() called outside setup context. ` +
201
+ `Key: ${String(key)}. This will fail silently.`
202
+ )
203
+ return
204
+ }
205
+
206
+ provide(key, value)
207
+ }
208
+ ```
209
+
210
+ ## App-Level Provide (Exception)
211
+
212
+ `app.provide()` can be called anytime during app initialization:
213
+
214
+ ```js
215
+ // main.js
216
+ import { createApp } from 'vue'
217
+ import App from './App.vue'
218
+
219
+ const app = createApp(App)
220
+
221
+ // This works - app-level provide
222
+ app.provide('appConfig', { version: '1.0.0' })
223
+
224
+ // Even async is OK at app level before mount
225
+ fetchConfig().then(config => {
226
+ app.provide('apiConfig', config)
227
+ app.mount('#app')
228
+ })
229
+ ```
230
+
231
+ But once the app is mounted, `app.provide()` should not be called.
232
+
233
+ ## Reference
234
+ - [Vue.js Composition API - provide()](https://vuejs.org/api/composition-api-dependency-injection.html#provide)
235
+ - [Vue.js Provide/Inject Guide](https://vuejs.org/guide/components/provide-inject.html)
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: Never Destructure reactive() Objects Directly
3
+ impact: HIGH
4
+ impactDescription: Destructuring reactive objects breaks reactivity - changes won't trigger updates
5
+ type: capability
6
+ tags: [vue3, reactivity, reactive, composition-api, destructuring]
7
+ ---
8
+
9
+ # Never Destructure reactive() Objects Directly
10
+
11
+ **Impact: HIGH** - Destructuring a `reactive()` object breaks the reactive connection. Updates to destructured variables won't trigger UI updates, leading to stale data display.
12
+
13
+ Vue's `reactive()` uses JavaScript Proxies to track property access. When you destructure, you extract primitive values from the proxy, losing the reactive connection. This is especially dangerous when destructuring from composables or imported state.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Never destructure reactive objects directly if you need reactivity
18
+ - [ ] Use `toRefs()` to convert reactive object properties to refs before destructuring
19
+ - [ ] Consider using `ref()` instead of `reactive()` to avoid this pitfall entirely
20
+ - [ ] When importing state from composables, check if it's reactive before destructuring
21
+
22
+ **Incorrect:**
23
+ ```javascript
24
+ import { reactive } from 'vue'
25
+
26
+ const state = reactive({
27
+ count: 0,
28
+ name: 'Vue'
29
+ })
30
+
31
+ // WRONG: Destructuring breaks reactivity
32
+ const { count, name } = state
33
+
34
+ // These updates work on the original state...
35
+ state.count++ // state.count is now 1
36
+
37
+ // ...but the destructured variables are NOT updated
38
+ console.log(count) // Still 0! Lost reactivity
39
+ ```
40
+
41
+ ```javascript
42
+ // WRONG: Destructuring from a composable
43
+ function useCounter() {
44
+ const state = reactive({ count: 0 })
45
+ return state
46
+ }
47
+
48
+ const { count } = useCounter() // count is now a non-reactive primitive
49
+ ```
50
+
51
+ **Correct:**
52
+ ```javascript
53
+ import { reactive, toRefs } from 'vue'
54
+
55
+ const state = reactive({
56
+ count: 0,
57
+ name: 'Vue'
58
+ })
59
+
60
+ // CORRECT: Use toRefs() to maintain reactivity
61
+ const { count, name } = toRefs(state)
62
+
63
+ state.count++
64
+ console.log(count.value) // 1 - Reactivity preserved! (note: now needs .value)
65
+ ```
66
+
67
+ ```javascript
68
+ // CORRECT: Return toRefs from composables
69
+ function useCounter() {
70
+ const state = reactive({ count: 0 })
71
+ return toRefs(state) // Now safe to destructure
72
+ }
73
+
74
+ const { count } = useCounter() // count is now a ref, reactivity preserved
75
+ ```
76
+
77
+ ```javascript
78
+ // ALTERNATIVE: Just use ref() to avoid the issue entirely
79
+ import { ref } from 'vue'
80
+
81
+ const count = ref(0)
82
+ const name = ref('Vue')
83
+
84
+ // No destructuring needed, no gotchas
85
+ ```
86
+
87
+ ## Reference
88
+ - [Vue.js Reactivity Fundamentals - reactive()](https://vuejs.org/guide/essentials/reactivity-fundamentals.html#reactive)
89
+ - [Vue.js Reactivity API - toRefs()](https://vuejs.org/api/reactivity-utilities.html#torefs)
@@ -0,0 +1,132 @@
1
+ ---
2
+ title: Use Debug Hooks to Trace Reactivity Issues
3
+ impact: MEDIUM
4
+ impactDescription: Debug hooks help identify which dependencies trigger re-renders and watcher executions
5
+ type: efficiency
6
+ tags: [vue3, reactivity, debugging, computed, watch, development]
7
+ ---
8
+
9
+ # Use Debug Hooks to Trace Reactivity Issues
10
+
11
+ **Impact: MEDIUM** - Vue provides debug hooks (`onTrack`, `onTrigger`, `renderTracked`, `renderTriggered`) that help identify exactly which reactive dependencies are being tracked and which mutations trigger re-execution. These are invaluable for debugging performance issues and unexpected re-renders.
12
+
13
+ Debug hooks only work in development mode and are stripped in production builds. Use them to understand why a computed property, watcher, or component is re-executing.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Use `onTrack` and `onTrigger` options on computed/watch for granular debugging
18
+ - [ ] Use `onRenderTracked` and `onRenderTriggered` lifecycle hooks for component render debugging
19
+ - [ ] Add `debugger` statements inside hooks to pause execution and inspect state
20
+ - [ ] Remove or comment out debug hooks before production (they're no-ops but add clutter)
21
+
22
+ > **Note:** `onTrack` and `onTrigger` are development-only hooks. They are stripped from production builds and may not fire in test environments (e.g., Vitest, Jest) depending on how Vue is bundled. If you need to verify reactivity behavior in tests, use direct assertions on reactive state changes rather than relying on these debug callbacks.
23
+
24
+ **Debugging computed properties:**
25
+ ```javascript
26
+ import { ref, computed } from 'vue'
27
+
28
+ const count = ref(0)
29
+ const doubled = computed(() => count.value * 2, {
30
+ onTrack(event) {
31
+ // Called when a dependency is tracked
32
+ // event.target = the reactive object
33
+ // event.key = the property being accessed
34
+ debugger
35
+ console.log('Tracking:', event)
36
+ },
37
+ onTrigger(event) {
38
+ // Called when a dependency mutation triggers re-computation
39
+ debugger
40
+ console.log('Triggered by:', event)
41
+ }
42
+ })
43
+ ```
44
+
45
+ **Debugging watchers:**
46
+ ```javascript
47
+ import { ref, watch, watchEffect } from 'vue'
48
+
49
+ const source = ref(0)
50
+
51
+ // With watch()
52
+ watch(source, (newVal, oldVal) => {
53
+ console.log('Changed:', oldVal, '->', newVal)
54
+ }, {
55
+ onTrack(e) {
56
+ debugger // Pause to see what's being tracked
57
+ },
58
+ onTrigger(e) {
59
+ debugger // Pause to see what triggered the watcher
60
+ }
61
+ })
62
+
63
+ // With watchEffect()
64
+ watchEffect(() => {
65
+ console.log('Source is:', source.value)
66
+ }, {
67
+ onTrack(e) {
68
+ console.log('Tracking dependency:', e.key)
69
+ },
70
+ onTrigger(e) {
71
+ console.log('Triggered by:', e.key, 'mutation')
72
+ }
73
+ })
74
+ ```
75
+
76
+ **Debugging component renders:**
77
+ ```vue
78
+ <script setup>
79
+ import { onRenderTracked, onRenderTriggered, ref } from 'vue'
80
+
81
+ const count = ref(0)
82
+
83
+ // Called for every reactive dependency accessed during render
84
+ onRenderTracked((event) => {
85
+ console.log('Render tracked:', event.key, 'from', event.target)
86
+ debugger // Pause to inspect which dependencies are tracked
87
+ })
88
+
89
+ // Called when a reactive dependency triggers re-render
90
+ onRenderTriggered((event) => {
91
+ console.log('Render triggered by:', event.key)
92
+ console.log('Old value:', event.oldValue)
93
+ console.log('New value:', event.newValue)
94
+ debugger // Pause to see exactly what caused the re-render
95
+ })
96
+ </script>
97
+ ```
98
+
99
+ **Options API equivalent:**
100
+ ```javascript
101
+ export default {
102
+ data() {
103
+ return { count: 0 }
104
+ },
105
+ renderTracked(event) {
106
+ console.log('Dependency tracked during render:', event)
107
+ debugger
108
+ },
109
+ renderTriggered(event) {
110
+ console.log('Re-render triggered by:', event)
111
+ debugger
112
+ }
113
+ }
114
+ ```
115
+
116
+ **Debug event properties:**
117
+ ```javascript
118
+ // The event object contains:
119
+ {
120
+ effect: ReactiveEffect, // The effect being debugged
121
+ target: object, // The reactive object
122
+ type: 'get' | 'set' | 'add' | 'delete' | 'clear',
123
+ key: string | symbol, // The property being accessed/mutated
124
+ oldValue: any, // Previous value (for onTrigger)
125
+ newValue: any // New value (for onTrigger)
126
+ }
127
+ ```
128
+
129
+ ## Reference
130
+ - [Vue.js Reactivity in Depth - Debugging](https://vuejs.org/guide/extras/reactivity-in-depth.html#reactivity-debugging)
131
+ - [Vue.js computed() API](https://vuejs.org/api/reactivity-core.html#computed)
132
+ - [Vue.js onRenderTracked()](https://vuejs.org/api/composition-api-lifecycle.html#onrendertracked)
@@ -0,0 +1,149 @@
1
+ ---
2
+ title: Use markRaw() for Objects That Should Never Be Reactive
3
+ impact: MEDIUM
4
+ impactDescription: Library instances, DOM nodes, and complex objects cause overhead and bugs when wrapped in Vue proxies
5
+ type: efficiency
6
+ tags: [vue3, reactivity, markRaw, performance, external-libraries, dom]
7
+ ---
8
+
9
+ # Use markRaw() for Objects That Should Never Be Reactive
10
+
11
+ **Impact: MEDIUM** - Vue's `markRaw()` tells the reactivity system to never wrap an object in a Proxy. Use it for library instances, DOM nodes, class instances with internal state, and complex objects that Vue shouldn't track. This prevents unnecessary proxy overhead and avoids subtle bugs from double-proxying.
12
+
13
+ Without `markRaw()`, placing these objects inside reactive state causes Vue to wrap them in Proxies, which can break library internals, cause identity issues, and waste memory on objects that don't need change tracking.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Use `markRaw()` for third-party library instances (maps, charts, editors)
18
+ - [ ] Use `markRaw()` for DOM elements stored in reactive state
19
+ - [ ] Use `markRaw()` for class instances that manage their own state
20
+ - [ ] Use `markRaw()` for large static data that will never change
21
+ - [ ] Remember: markRaw only affects the root level - nested objects may still be proxied
22
+
23
+ **Incorrect:**
24
+ ```javascript
25
+ import { reactive, ref } from 'vue'
26
+ import mapboxgl from 'mapbox-gl'
27
+ import * as monaco from 'monaco-editor'
28
+
29
+ // WRONG: Library instances wrapped in Proxy
30
+ const state = reactive({
31
+ map: new mapboxgl.Map({ container: 'map' }), // Proxied!
32
+ editor: monaco.editor.create(element, {}), // Proxied!
33
+ })
34
+
35
+ // Problems:
36
+ // 1. Library's internal this references may break
37
+ // 2. Unnecessary memory overhead
38
+ // 3. Methods may not work correctly through proxy
39
+ // 4. Performance degradation
40
+
41
+ // WRONG: DOM elements in reactive state
42
+ const elements = reactive({
43
+ container: document.getElementById('app'), // Proxied DOM node!
44
+ })
45
+ ```
46
+
47
+ **Correct:**
48
+ ```javascript
49
+ import { reactive, markRaw, shallowRef } from 'vue'
50
+ import mapboxgl from 'mapbox-gl'
51
+ import * as monaco from 'monaco-editor'
52
+
53
+ // CORRECT: Mark library instances as raw
54
+ const state = reactive({
55
+ map: markRaw(new mapboxgl.Map({ container: 'map' })),
56
+ editor: markRaw(monaco.editor.create(element, {})),
57
+ })
58
+
59
+ // CORRECT: Or use shallowRef for mutable references
60
+ const map = shallowRef(null)
61
+ onMounted(() => {
62
+ map.value = markRaw(new mapboxgl.Map({ container: 'map' }))
63
+ })
64
+
65
+ // CORRECT: Large static data
66
+ const geoJsonData = markRaw(await fetch('/huge-geojson.json').then(r => r.json()))
67
+ const state = reactive({
68
+ mapData: geoJsonData // Won't be proxied
69
+ })
70
+ ```
71
+
72
+ **Class instances with internal state:**
73
+ ```javascript
74
+ import { markRaw, reactive } from 'vue'
75
+
76
+ class WebSocketManager {
77
+ constructor(url) {
78
+ this.socket = new WebSocket(url)
79
+ this.listeners = new Map()
80
+ }
81
+
82
+ on(event, callback) {
83
+ this.listeners.set(event, callback)
84
+ }
85
+ }
86
+
87
+ // CORRECT: Mark class instance
88
+ const wsManager = markRaw(new WebSocketManager('ws://example.com'))
89
+
90
+ const state = reactive({
91
+ connection: wsManager // Won't be proxied
92
+ })
93
+
94
+ // Can still use the instance normally
95
+ state.connection.on('message', handleMessage)
96
+ ```
97
+
98
+ **Gotcha: markRaw only affects root level:**
99
+ ```javascript
100
+ import { markRaw, reactive } from 'vue'
101
+
102
+ const rawObject = markRaw({
103
+ nested: { value: 1 } // This nested object is NOT marked raw
104
+ })
105
+
106
+ const state = reactive({
107
+ data: rawObject
108
+ })
109
+
110
+ // rawObject itself won't be proxied
111
+ // But if you access nested objects through a reactive parent:
112
+ const container = reactive({ raw: rawObject })
113
+ // container.raw.nested might still be proxied in some cases
114
+
115
+ // SAFER: Use shallowRef for the container
116
+ import { shallowRef } from 'vue'
117
+ const safeContainer = shallowRef(rawObject)
118
+ ```
119
+
120
+ **Combining with shallowRef for best results:**
121
+ ```javascript
122
+ import { shallowRef, markRaw, onMounted, onUnmounted } from 'vue'
123
+
124
+ // Pattern: shallowRef + markRaw for external library instances
125
+ export function useMapbox(containerId) {
126
+ const map = shallowRef(null)
127
+
128
+ onMounted(() => {
129
+ const instance = new mapboxgl.Map({
130
+ container: containerId,
131
+ style: 'mapbox://styles/mapbox/streets-v11'
132
+ })
133
+
134
+ // Mark raw to prevent any proxy wrapping
135
+ map.value = markRaw(instance)
136
+ })
137
+
138
+ onUnmounted(() => {
139
+ map.value?.remove()
140
+ })
141
+
142
+ return { map }
143
+ }
144
+ ```
145
+
146
+ ## Reference
147
+ - [Vue.js markRaw() API](https://vuejs.org/api/reactivity-advanced.html#markraw)
148
+ - [Vue.js Reducing Reactivity Overhead](https://vuejs.org/guide/best-practices/performance.html#reduce-reactivity-overhead-for-large-immutable-structures)
149
+ - [Vue.js Reactivity in Depth](https://vuejs.org/guide/extras/reactivity-in-depth.html)
@@ -0,0 +1,96 @@
1
+ ---
2
+ title: Avoid Comparing Reactive Objects with === Operator
3
+ impact: HIGH
4
+ impactDescription: Reactive proxies have different identity than original objects - comparison bugs are silent and hard to debug
5
+ type: gotcha
6
+ tags: [vue3, reactivity, proxy, comparison, debugging, identity]
7
+ ---
8
+
9
+ # Avoid Comparing Reactive Objects with === Operator
10
+
11
+ **Impact: HIGH** - Vue's `reactive()` returns a Proxy wrapper that has a different identity than the original object. Using `===` to compare reactive objects can lead to silent bugs where comparisons unexpectedly return `false`.
12
+
13
+ When you wrap an object with `reactive()`, the returned proxy is NOT equal to the original object. Additionally, accessing nested objects from a reactive object returns new proxy wrappers each time, which can cause identity comparison issues.
14
+
15
+ ## Task Checklist
16
+
17
+ - [ ] Never compare reactive object instances with `===` directly
18
+ - [ ] Use unique identifiers (ID, UUID) for object comparison instead
19
+ - [ ] Use `toRaw()` on both sides when identity comparison is absolutely necessary
20
+ - [ ] Consider using primitive identifiers from database records for comparison
21
+
22
+ **Incorrect:**
23
+ ```javascript
24
+ import { reactive } from 'vue'
25
+
26
+ const original = { id: 1, name: 'Item' }
27
+ const state = reactive(original)
28
+
29
+ // BUG: Always returns false - proxy !== original
30
+ if (state === original) {
31
+ console.log('Same object') // Never executes
32
+ }
33
+
34
+ // BUG: Nested object comparison fails
35
+ const items = reactive([{ id: 1 }, { id: 2 }])
36
+ const item = items[0]
37
+
38
+ // Later...
39
+ if (items[0] === item) {
40
+ // May or may not work depending on Vue's proxy caching
41
+ }
42
+
43
+ // BUG: Comparing items from different reactive sources
44
+ const listA = reactive([{ id: 1 }])
45
+ const listB = reactive([{ id: 1 }])
46
+ if (listA[0] === listB[0]) {
47
+ // Never true, even though they represent the same data
48
+ }
49
+ ```
50
+
51
+ **Correct:**
52
+ ```javascript
53
+ import { reactive, toRaw } from 'vue'
54
+
55
+ const original = { id: 1, name: 'Item' }
56
+ const state = reactive(original)
57
+
58
+ // CORRECT: Use toRaw() for identity comparison
59
+ if (toRaw(state) === original) {
60
+ console.log('Same underlying object') // Works!
61
+ }
62
+
63
+ // BEST: Use unique identifiers instead
64
+ const items = reactive([
65
+ { id: 'uuid-1', name: 'Item 1' },
66
+ { id: 'uuid-2', name: 'Item 2' }
67
+ ])
68
+
69
+ function findItem(targetId) {
70
+ return items.find(item => item.id === targetId)
71
+ }
72
+
73
+ function isSelected(item) {
74
+ return selectedId.value === item.id // Compare IDs, not objects
75
+ }
76
+
77
+ // CORRECT: For Set/Map operations, use primitive keys
78
+ const selectedIds = reactive(new Set())
79
+ selectedIds.add(item.id) // Use ID, not object
80
+ selectedIds.has(item.id) // Check by ID
81
+ ```
82
+
83
+ ```javascript
84
+ // When you must compare objects, use toRaw on both sides
85
+ import { toRaw, isReactive } from 'vue'
86
+
87
+ function areEqual(a, b) {
88
+ const rawA = isReactive(a) ? toRaw(a) : a
89
+ const rawB = isReactive(b) ? toRaw(b) : b
90
+ return rawA === rawB
91
+ }
92
+ ```
93
+
94
+ ## Reference
95
+ - [Vue.js Reactivity in Depth](https://vuejs.org/guide/extras/reactivity-in-depth.html)
96
+ - [Vue.js toRaw() API](https://vuejs.org/api/reactivity-advanced.html#toraw)