@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,218 @@
1
+ ---
2
+ title: Use shallowRef for Dynamic Component References
3
+ impact: MEDIUM
4
+ impactDescription: Storing components in reactive() or ref() triggers Vue warnings and can cause performance issues
5
+ type: gotcha
6
+ tags: [typescript, shallowRef, dynamic-components, reactivity, performance]
7
+ ---
8
+
9
+ # Use shallowRef for Dynamic Component References
10
+
11
+ **Impact: MEDIUM** - When storing Vue components in reactive state for dynamic rendering, using `ref()` or `reactive()` causes Vue warnings and unnecessary reactivity overhead. Use `shallowRef()` instead.
12
+
13
+ ## Task Checklist
14
+
15
+ - [ ] Use `shallowRef` for storing component references
16
+ - [ ] Use `markRaw` when storing components in reactive objects
17
+ - [ ] Avoid wrapping component definitions with deep reactivity
18
+ - [ ] Check console for "[Vue warn]: Vue received a Component that was made a reactive object"
19
+
20
+ ## The Problem
21
+
22
+ Vue components are objects with internal properties that should not be made reactive. When you store a component in `ref()` or `reactive()`, Vue traverses all properties deeply, which:
23
+
24
+ 1. Triggers a console warning
25
+ 2. Creates unnecessary reactive proxies
26
+ 3. Can cause subtle bugs with component identity
27
+ 4. Impacts performance
28
+
29
+ **Incorrect - Using ref() for components:**
30
+ ```typescript
31
+ import { ref } from 'vue'
32
+ import ComponentA from './ComponentA.vue'
33
+ import ComponentB from './ComponentB.vue'
34
+
35
+ // BAD: Vue will warn about making component reactive
36
+ const currentComponent = ref(ComponentA)
37
+
38
+ function switchComponent() {
39
+ currentComponent.value = ComponentB
40
+ }
41
+ ```
42
+
43
+ **Console warning:**
44
+ ```
45
+ [Vue warn]: Vue received a Component that was made a reactive object.
46
+ This can lead to unnecessary performance overhead and should be avoided
47
+ by marking the component with `markRaw` or using `shallowRef` instead of `ref`.
48
+ ```
49
+
50
+ ## Solution 1: Use shallowRef
51
+
52
+ `shallowRef` only makes the `.value` reference reactive, not the contents:
53
+
54
+ ```typescript
55
+ import { shallowRef, type Component } from 'vue'
56
+ import ComponentA from './ComponentA.vue'
57
+ import ComponentB from './ComponentB.vue'
58
+
59
+ // CORRECT: shallowRef doesn't deep-proxy the component
60
+ const currentComponent = shallowRef<Component>(ComponentA)
61
+
62
+ function switchComponent() {
63
+ currentComponent.value = ComponentB
64
+ }
65
+ ```
66
+
67
+ ```vue
68
+ <template>
69
+ <component :is="currentComponent" />
70
+ </template>
71
+ ```
72
+
73
+ ## Solution 2: Use markRaw in Reactive Objects
74
+
75
+ When components are part of a larger reactive object:
76
+
77
+ ```typescript
78
+ import { reactive, markRaw, type Component } from 'vue'
79
+ import TabHome from './TabHome.vue'
80
+ import TabProfile from './TabProfile.vue'
81
+ import TabSettings from './TabSettings.vue'
82
+
83
+ interface Tab {
84
+ name: string
85
+ component: Component
86
+ }
87
+
88
+ // CORRECT: markRaw prevents reactivity on component objects
89
+ const tabs = reactive<Tab[]>([
90
+ { name: 'Home', component: markRaw(TabHome) },
91
+ { name: 'Profile', component: markRaw(TabProfile) },
92
+ { name: 'Settings', component: markRaw(TabSettings) }
93
+ ])
94
+
95
+ const activeTab = shallowRef<Tab>(tabs[0])
96
+ ```
97
+
98
+ ```vue
99
+ <template>
100
+ <div class="tabs">
101
+ <button
102
+ v-for="tab in tabs"
103
+ :key="tab.name"
104
+ @click="activeTab = tab"
105
+ >
106
+ {{ tab.name }}
107
+ </button>
108
+ </div>
109
+ <component :is="activeTab.component" />
110
+ </template>
111
+ ```
112
+
113
+ ## TypeScript Typing
114
+
115
+ For proper TypeScript support with dynamic components:
116
+
117
+ ```typescript
118
+ import { shallowRef, type Component, type DefineComponent } from 'vue'
119
+
120
+ // Generic component type
121
+ const currentComponent = shallowRef<Component | null>(null)
122
+
123
+ // Or more specific with props
124
+ interface MyComponentProps {
125
+ title: string
126
+ }
127
+
128
+ const currentComponent = shallowRef<DefineComponent<MyComponentProps> | null>(null)
129
+ ```
130
+
131
+ ## Dynamic Import with shallowRef
132
+
133
+ When using dynamic imports for code splitting:
134
+
135
+ ```typescript
136
+ import { shallowRef, defineAsyncComponent, type Component } from 'vue'
137
+
138
+ const currentComponent = shallowRef<Component | null>(null)
139
+
140
+ async function loadComponent(name: string) {
141
+ const component = defineAsyncComponent(
142
+ () => import(`./components/${name}.vue`)
143
+ )
144
+ currentComponent.value = component
145
+ }
146
+ ```
147
+
148
+ ## Component Registry Pattern
149
+
150
+ For tab systems or wizard-like interfaces:
151
+
152
+ ```typescript
153
+ import { shallowRef, markRaw, type Component } from 'vue'
154
+
155
+ // Type-safe component registry
156
+ const componentRegistry = {
157
+ home: markRaw(defineAsyncComponent(() => import('./Home.vue'))),
158
+ about: markRaw(defineAsyncComponent(() => import('./About.vue'))),
159
+ contact: markRaw(defineAsyncComponent(() => import('./Contact.vue')))
160
+ } as const
161
+
162
+ type ComponentKey = keyof typeof componentRegistry
163
+
164
+ const currentView = shallowRef<ComponentKey>('home')
165
+
166
+ // Computed to get current component
167
+ const currentComponent = computed(() => componentRegistry[currentView.value])
168
+ ```
169
+
170
+ ```vue
171
+ <template>
172
+ <component :is="currentComponent" />
173
+ </template>
174
+ ```
175
+
176
+ ## When to Use Each Approach
177
+
178
+ | Scenario | Solution |
179
+ |----------|----------|
180
+ | Single dynamic component reference | `shallowRef` |
181
+ | Component in reactive array/object | `markRaw` on component |
182
+ | Component map/registry | `markRaw` each component |
183
+ | Async components | `defineAsyncComponent` + `shallowRef` |
184
+
185
+ ## Common Mistakes
186
+
187
+ ### Mistake 1: Using computed with ref
188
+
189
+ ```typescript
190
+ // BAD: Still triggers warning
191
+ const components = ref([ComponentA, ComponentB])
192
+ const current = computed(() => components.value[index.value])
193
+
194
+ // GOOD: Use shallowRef for the array
195
+ const components = shallowRef([ComponentA, ComponentB])
196
+ ```
197
+
198
+ ### Mistake 2: Forgetting markRaw in map
199
+
200
+ ```typescript
201
+ // BAD: Components in map become reactive
202
+ const routes = reactive(new Map([
203
+ ['home', HomeComponent],
204
+ ['about', AboutComponent]
205
+ ]))
206
+
207
+ // GOOD: Mark each component as raw
208
+ const routes = reactive(new Map([
209
+ ['home', markRaw(HomeComponent)],
210
+ ['about', markRaw(AboutComponent)]
211
+ ]))
212
+ ```
213
+
214
+ ## Reference
215
+
216
+ - [Vue.js Reactivity in Depth - Reducing Reactivity Overhead](https://vuejs.org/guide/extras/reactivity-in-depth.html#reducing-reactivity-overhead-for-large-immutable-structures)
217
+ - [Vue.js API - shallowRef](https://vuejs.org/api/reactivity-advanced.html#shallowref)
218
+ - [Vue.js API - markRaw](https://vuejs.org/api/reactivity-advanced.html#markraw)
@@ -0,0 +1,249 @@
1
+ ---
2
+ title: Template Refs Are Null Until Mounted
3
+ impact: HIGH
4
+ impactDescription: Accessing template ref before mount or after unmount causes runtime errors
5
+ type: gotcha
6
+ tags: [vue3, typescript, template-refs, lifecycle, null-safety]
7
+ ---
8
+
9
+ # Template Refs Are Null Until Mounted
10
+
11
+ **Impact: HIGH** - Template refs have an initial value of `null` and remain null until the component mounts. They can also become null again if the referenced element is removed by `v-if`. Always account for this in TypeScript with union types and optional chaining.
12
+
13
+ ## Task Checklist
14
+
15
+ - [ ] Always type template refs with `| null` union
16
+ - [ ] Only access refs inside `onMounted` or after
17
+ - [ ] Use optional chaining (`?.`) when accessing ref properties
18
+ - [ ] Handle `v-if` scenarios where ref can become null again
19
+ - [ ] Consider using `useTemplateRef` in Vue 3.5+
20
+
21
+ ## The Problem
22
+
23
+ ```vue
24
+ <script setup lang="ts">
25
+ import { ref } from 'vue'
26
+
27
+ // WRONG: Doesn't account for null
28
+ const inputRef = ref<HTMLInputElement>()
29
+
30
+ // WRONG: Will crash if accessed before mount
31
+ inputRef.value.focus() // Error: Cannot read properties of null
32
+
33
+ // WRONG: Accessed in setup, element doesn't exist yet
34
+ console.log(inputRef.value.value) // Error!
35
+ </script>
36
+
37
+ <template>
38
+ <input ref="inputRef" />
39
+ </template>
40
+ ```
41
+
42
+ ## The Solution
43
+
44
+ ```vue
45
+ <script setup lang="ts">
46
+ import { ref, onMounted } from 'vue'
47
+
48
+ // CORRECT: Include null in the type
49
+ const inputRef = ref<HTMLInputElement | null>(null)
50
+
51
+ // CORRECT: Access in onMounted when DOM exists
52
+ onMounted(() => {
53
+ inputRef.value?.focus() // Safe with optional chaining
54
+ })
55
+
56
+ // CORRECT: Guard before accessing
57
+ function focusInput() {
58
+ if (inputRef.value) {
59
+ inputRef.value.focus()
60
+ }
61
+ }
62
+ </script>
63
+
64
+ <template>
65
+ <input ref="inputRef" />
66
+ </template>
67
+ ```
68
+
69
+ ## Vue 3.5+: useTemplateRef
70
+
71
+ Vue 3.5 introduces `useTemplateRef` with better type inference:
72
+
73
+ ```vue
74
+ <script setup lang="ts">
75
+ import { useTemplateRef, onMounted } from 'vue'
76
+
77
+ // Type is automatically inferred for static refs
78
+ const inputRef = useTemplateRef<HTMLInputElement>('input')
79
+
80
+ onMounted(() => {
81
+ inputRef.value?.focus()
82
+ })
83
+ </script>
84
+
85
+ <template>
86
+ <input ref="input" />
87
+ </template>
88
+ ```
89
+
90
+ ## Handling v-if Scenarios
91
+
92
+ Refs can become `null` when elements are conditionally rendered:
93
+
94
+ ```vue
95
+ <script setup lang="ts">
96
+ import { ref, watch } from 'vue'
97
+
98
+ const showModal = ref(false)
99
+ const modalRef = ref<HTMLDivElement | null>(null)
100
+
101
+ // WRONG: Assuming ref always exists after first mount
102
+ function closeModal() {
103
+ modalRef.value.classList.remove('open') // May be null!
104
+ }
105
+
106
+ // CORRECT: Always guard access
107
+ function closeModal() {
108
+ modalRef.value?.classList.remove('open')
109
+ }
110
+
111
+ // CORRECT: Watch for ref changes
112
+ watch(modalRef, (newRef) => {
113
+ if (newRef) {
114
+ // Modal element just mounted
115
+ newRef.focus()
116
+ }
117
+ // If null, modal was unmounted
118
+ })
119
+ </script>
120
+
121
+ <template>
122
+ <div v-if="showModal" ref="modalRef" class="modal">
123
+ Modal content
124
+ </div>
125
+ </template>
126
+ ```
127
+
128
+ ## Component Refs
129
+
130
+ For component refs, use `InstanceType`:
131
+
132
+ ```vue
133
+ <script setup lang="ts">
134
+ import { ref, onMounted } from 'vue'
135
+ import ChildComponent from './ChildComponent.vue'
136
+
137
+ // Component ref with null
138
+ const childRef = ref<InstanceType<typeof ChildComponent> | null>(null)
139
+
140
+ onMounted(() => {
141
+ // Access exposed methods/properties
142
+ childRef.value?.exposedMethod()
143
+ })
144
+ </script>
145
+
146
+ <template>
147
+ <ChildComponent ref="childRef" />
148
+ </template>
149
+ ```
150
+
151
+ Remember: Child components must use `defineExpose` to expose methods:
152
+
153
+ ```vue
154
+ <!-- ChildComponent.vue -->
155
+ <script setup lang="ts">
156
+ function exposedMethod() {
157
+ console.log('Called from parent')
158
+ }
159
+
160
+ defineExpose({
161
+ exposedMethod
162
+ })
163
+ </script>
164
+ ```
165
+
166
+ ## Multiple Refs with v-for
167
+
168
+ ```vue
169
+ <script setup lang="ts">
170
+ import { ref, onMounted } from 'vue'
171
+
172
+ const items = ref(['a', 'b', 'c'])
173
+
174
+ // Array of refs for v-for
175
+ const itemRefs = ref<(HTMLLIElement | null)[]>([])
176
+
177
+ onMounted(() => {
178
+ // Access specific item
179
+ itemRefs.value[0]?.focus()
180
+
181
+ // Iterate safely
182
+ itemRefs.value.forEach(el => {
183
+ el?.classList.add('mounted')
184
+ })
185
+ })
186
+ </script>
187
+
188
+ <template>
189
+ <ul>
190
+ <li
191
+ v-for="(item, index) in items"
192
+ :key="item"
193
+ :ref="el => { itemRefs[index] = el as HTMLLIElement }"
194
+ >
195
+ {{ item }}
196
+ </li>
197
+ </ul>
198
+ </template>
199
+ ```
200
+
201
+ ## Async Operations and Refs
202
+
203
+ Be careful with async operations:
204
+
205
+ ```vue
206
+ <script setup lang="ts">
207
+ import { ref, onMounted } from 'vue'
208
+
209
+ const containerRef = ref<HTMLDivElement | null>(null)
210
+
211
+ onMounted(async () => {
212
+ // containerRef.value exists here
213
+
214
+ await fetchData()
215
+
216
+ // CAREFUL: Component might have unmounted during await
217
+ // Always re-check before accessing
218
+ if (containerRef.value) {
219
+ containerRef.value.scrollTop = 0
220
+ }
221
+ })
222
+ </script>
223
+ ```
224
+
225
+ ## Type Guard Pattern
226
+
227
+ Create a reusable type guard for cleaner code:
228
+
229
+ ```typescript
230
+ // utils/refs.ts
231
+ export function assertRef<T>(
232
+ ref: Ref<T | null>,
233
+ message = 'Ref is not available'
234
+ ): asserts ref is Ref<T> {
235
+ if (ref.value === null) {
236
+ throw new Error(message)
237
+ }
238
+ }
239
+
240
+ // Usage in component
241
+ function mustFocus() {
242
+ assertRef(inputRef, 'Input element not mounted')
243
+ inputRef.value.focus() // TypeScript knows it's not null here
244
+ }
245
+ ```
246
+
247
+ ## Reference
248
+ - [Vue.js TypeScript with Composition API - Template Refs](https://vuejs.org/guide/typescript/composition-api.html#typing-template-refs)
249
+ - [Vue.js Template Refs](https://vuejs.org/guide/essentials/template-refs.html)
@@ -0,0 +1,214 @@
1
+ ---
2
+ title: Use Type Casting in Templates for Union Types
3
+ impact: MEDIUM
4
+ impactDescription: Template expressions with union types cause TypeScript errors even when the runtime type is known
5
+ type: gotcha
6
+ tags: [typescript, templates, type-casting, union-types, script-setup]
7
+ ---
8
+
9
+ # Use Type Casting in Templates for Union Types
10
+
11
+ **Impact: MEDIUM** - When using `lang="ts"` in Vue Single File Components, template expressions get strict type checking. If a variable has a union type, you may need inline type casting to access type-specific methods or properties.
12
+
13
+ ## Task Checklist
14
+
15
+ - [ ] Use `(value as Type)` syntax for type casting in templates
16
+ - [ ] Consider narrowing types in script before using in template
17
+ - [ ] Remember template type checking requires `lang="ts"` attribute
18
+ - [ ] For Vue CLI/webpack: ensure vue-loader >= 16.8.0
19
+
20
+ ## The Problem
21
+
22
+ When a variable has a union type, TypeScript cannot know which specific type it is at template compile time:
23
+
24
+ **Template with type error:**
25
+ ```vue
26
+ <script setup lang="ts">
27
+ // Union type: could be string OR number
28
+ let x: string | number = 1
29
+ </script>
30
+
31
+ <template>
32
+ <!-- ERROR: Property 'toFixed' does not exist on type 'string | number' -->
33
+ {{ x.toFixed(2) }}
34
+ </template>
35
+ ```
36
+
37
+ TypeScript correctly identifies that `toFixed()` only exists on `number`, not `string`.
38
+
39
+ ## Solution 1: Inline Type Casting
40
+
41
+ Use `(value as Type)` syntax directly in the template:
42
+
43
+ ```vue
44
+ <script setup lang="ts">
45
+ let x: string | number = 1
46
+ </script>
47
+
48
+ <template>
49
+ <!-- CORRECT: Cast to number to access toFixed -->
50
+ {{ (x as number).toFixed(2) }}
51
+ </template>
52
+ ```
53
+
54
+ ## Solution 2: Computed Property (Preferred)
55
+
56
+ Create a computed property that narrows or transforms the type:
57
+
58
+ ```vue
59
+ <script setup lang="ts">
60
+ import { ref, computed } from 'vue'
61
+
62
+ const value = ref<string | number>(1)
63
+
64
+ const formattedValue = computed(() => {
65
+ if (typeof value.value === 'number') {
66
+ return value.value.toFixed(2)
67
+ }
68
+ return value.value
69
+ })
70
+ </script>
71
+
72
+ <template>
73
+ <!-- Clean template, type-safe logic in script -->
74
+ {{ formattedValue }}
75
+ </template>
76
+ ```
77
+
78
+ ## Solution 3: Type Guard Function
79
+
80
+ Define a type guard and use it in the template:
81
+
82
+ ```vue
83
+ <script setup lang="ts">
84
+ import { ref } from 'vue'
85
+
86
+ const data = ref<string | number | null>(null)
87
+
88
+ function isNumber(val: unknown): val is number {
89
+ return typeof val === 'number'
90
+ }
91
+
92
+ function formatNumber(val: number): string {
93
+ return val.toFixed(2)
94
+ }
95
+ </script>
96
+
97
+ <template>
98
+ <div v-if="isNumber(data)">
99
+ {{ formatNumber(data) }}
100
+ </div>
101
+ <div v-else-if="data !== null">
102
+ {{ data }}
103
+ </div>
104
+ </template>
105
+ ```
106
+
107
+ ## Common Use Cases
108
+
109
+ ### API Response Data
110
+
111
+ ```vue
112
+ <script setup lang="ts">
113
+ interface ApiResponse {
114
+ status: 'success' | 'error'
115
+ data?: UserData
116
+ error?: string
117
+ }
118
+
119
+ const response = ref<ApiResponse | null>(null)
120
+ </script>
121
+
122
+ <template>
123
+ <div v-if="response?.status === 'success'">
124
+ <!-- Cast to access data safely -->
125
+ {{ (response as { data: UserData }).data.name }}
126
+ </div>
127
+ </template>
128
+ ```
129
+
130
+ **Better approach with computed:**
131
+ ```vue
132
+ <script setup lang="ts">
133
+ const userData = computed(() => {
134
+ if (response.value?.status === 'success') {
135
+ return response.value.data
136
+ }
137
+ return null
138
+ })
139
+ </script>
140
+
141
+ <template>
142
+ <div v-if="userData">
143
+ {{ userData.name }}
144
+ </div>
145
+ </template>
146
+ ```
147
+
148
+ ### Event Handlers with Event Types
149
+
150
+ ```vue
151
+ <script setup lang="ts">
152
+ function handleInput(event: Event) {
153
+ // Cast to HTMLInputElement to access 'value'
154
+ const value = (event.target as HTMLInputElement).value
155
+ console.log(value)
156
+ }
157
+ </script>
158
+
159
+ <template>
160
+ <input @input="handleInput" />
161
+ </template>
162
+ ```
163
+
164
+ ### Array Item Access
165
+
166
+ ```vue
167
+ <script setup lang="ts">
168
+ const items = ref<(string | number)[]>([1, 'two', 3])
169
+ </script>
170
+
171
+ <template>
172
+ <ul>
173
+ <li v-for="item in items" :key="item">
174
+ <!-- Cast when you know the type -->
175
+ <span v-if="typeof item === 'number'">
176
+ Number: {{ (item as number).toFixed(1) }}
177
+ </span>
178
+ <span v-else>
179
+ String: {{ (item as string).toUpperCase() }}
180
+ </span>
181
+ </li>
182
+ </ul>
183
+ </template>
184
+ ```
185
+
186
+ ## When Type Casting is Needed
187
+
188
+ | Scenario | Solution |
189
+ |----------|----------|
190
+ | Union types | Cast to specific type |
191
+ | Nullable types | Use optional chaining or cast after null check |
192
+ | Event targets | Cast `event.target` to specific element type |
193
+ | Array methods | Cast when TS can't narrow array item types |
194
+
195
+ ## Important Notes
196
+
197
+ ### Template Type Checking Requirements
198
+
199
+ Template type checking is enabled when:
200
+ 1. `<script lang="ts">` or `<script setup lang="ts">` is used
201
+ 2. Vue Language Server (Volar) is active in your IDE
202
+ 3. For webpack: vue-loader >= 16.8.0 is required
203
+
204
+ ### Avoid Excessive Casting
205
+
206
+ If you find yourself casting frequently in templates, consider:
207
+ - Moving logic to computed properties
208
+ - Using type guards in the script section
209
+ - Refactoring data structures to be more specific
210
+
211
+ ## Reference
212
+
213
+ - [Vue.js TypeScript Overview - TypeScript in Templates](https://vuejs.org/guide/typescript/overview.html#typescript-in-templates)
214
+ - [Vue.js TypeScript with Composition API](https://vuejs.org/guide/typescript/composition-api.html)