@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.
- package/README.md +63 -0
- package/index.mjs +139 -0
- package/package.json +34 -0
- package/skills/create-adaptable-composable/SKILL.md +76 -0
- package/skills/vue-best-practices/SKILL.md +154 -0
- package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
- package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
- package/skills/vue-best-practices/references/component-async.md +97 -0
- package/skills/vue-best-practices/references/component-data-flow.md +350 -0
- package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
- package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
- package/skills/vue-best-practices/references/component-slots.md +216 -0
- package/skills/vue-best-practices/references/component-suspense.md +228 -0
- package/skills/vue-best-practices/references/component-teleport.md +108 -0
- package/skills/vue-best-practices/references/component-transition-group.md +128 -0
- package/skills/vue-best-practices/references/component-transition.md +125 -0
- package/skills/vue-best-practices/references/composables.md +290 -0
- package/skills/vue-best-practices/references/directives.md +162 -0
- package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
- package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
- package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
- package/skills/vue-best-practices/references/plugins.md +166 -0
- package/skills/vue-best-practices/references/reactivity.md +346 -0
- package/skills/vue-best-practices/references/render-functions.md +201 -0
- package/skills/vue-best-practices/references/sfc.md +310 -0
- package/skills/vue-best-practices/references/state-management.md +135 -0
- package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
- package/skills/vue-debug-guides/SKILL.md +205 -0
- package/skills/vue-debug-guides/reference/animation-key-for-rerender.md +160 -0
- package/skills/vue-debug-guides/reference/animation-transitiongroup-performance.md +241 -0
- package/skills/vue-debug-guides/reference/async-component-error-handling.md +115 -0
- package/skills/vue-debug-guides/reference/async-component-keepalive-ref-issue.md +112 -0
- package/skills/vue-debug-guides/reference/async-component-suspense-control.md +84 -0
- package/skills/vue-debug-guides/reference/async-component-vue-router.md +109 -0
- package/skills/vue-debug-guides/reference/attrs-event-listener-merging.md +205 -0
- package/skills/vue-debug-guides/reference/checkbox-true-false-value-form-submission.md +118 -0
- package/skills/vue-debug-guides/reference/cleanup-side-effects.md +172 -0
- package/skills/vue-debug-guides/reference/click-events-on-components.md +180 -0
- package/skills/vue-debug-guides/reference/component-naming-conflicts.md +159 -0
- package/skills/vue-debug-guides/reference/component-ref-requires-defineexpose.md +176 -0
- package/skills/vue-debug-guides/reference/composable-avoid-hidden-side-effects.md +208 -0
- package/skills/vue-debug-guides/reference/composable-call-location-restrictions.md +141 -0
- package/skills/vue-debug-guides/reference/composable-naming-return-pattern.md +139 -0
- package/skills/vue-debug-guides/reference/composable-tovalue-inside-watcheffect.md +182 -0
- package/skills/vue-debug-guides/reference/composition-api-not-functional-programming.md +120 -0
- package/skills/vue-debug-guides/reference/composition-api-script-setup-async-context.md +203 -0
- package/skills/vue-debug-guides/reference/composition-api-vs-react-hooks-differences.md +156 -0
- package/skills/vue-debug-guides/reference/computed-array-mutation.md +148 -0
- package/skills/vue-debug-guides/reference/computed-conditional-dependencies.md +147 -0
- package/skills/vue-debug-guides/reference/computed-no-parameters.md +159 -0
- package/skills/vue-debug-guides/reference/computed-no-side-effects.md +107 -0
- package/skills/vue-debug-guides/reference/computed-return-value-readonly.md +160 -0
- package/skills/vue-debug-guides/reference/configure-app-before-mount.md +89 -0
- package/skills/vue-debug-guides/reference/declare-emits-for-documentation.md +212 -0
- package/skills/vue-debug-guides/reference/define-expose-before-await.md +192 -0
- package/skills/vue-debug-guides/reference/define-model-default-value-sync.md +139 -0
- package/skills/vue-debug-guides/reference/defineEmits-must-be-top-level.md +164 -0
- package/skills/vue-debug-guides/reference/defineEmits-no-runtime-and-type-mixed.md +170 -0
- package/skills/vue-debug-guides/reference/definemodel-object-mutation-no-emit.md +148 -0
- package/skills/vue-debug-guides/reference/dom-update-timing-nexttick.md +90 -0
- package/skills/vue-debug-guides/reference/dynamic-argument-constraints.md +146 -0
- package/skills/vue-debug-guides/reference/dynamic-component-registration-vite.md +147 -0
- package/skills/vue-debug-guides/reference/event-modifier-order-matters.md +101 -0
- package/skills/vue-debug-guides/reference/exact-modifier-for-precise-shortcuts.md +155 -0
- package/skills/vue-debug-guides/reference/fallthrough-attrs-overwrite-vue3.md +159 -0
- package/skills/vue-debug-guides/reference/in-dom-template-parsing-caveats.md +149 -0
- package/skills/vue-debug-guides/reference/inheritattrs-false-for-wrapper-components.md +230 -0
- package/skills/vue-debug-guides/reference/keepalive-router-nested-double-mount.md +222 -0
- package/skills/vue-debug-guides/reference/keepalive-transition-memory-leak.md +144 -0
- package/skills/vue-debug-guides/reference/keyup-modifier-timing.md +137 -0
- package/skills/vue-debug-guides/reference/lifecycle-dom-access-timing.md +216 -0
- package/skills/vue-debug-guides/reference/lifecycle-hooks-synchronous-registration.md +156 -0
- package/skills/vue-debug-guides/reference/lifecycle-ssr-awareness.md +184 -0
- package/skills/vue-debug-guides/reference/local-components-not-in-descendants.md +151 -0
- package/skills/vue-debug-guides/reference/mount-return-value.md +88 -0
- package/skills/vue-debug-guides/reference/multi-root-component-class-attrs.md +93 -0
- package/skills/vue-debug-guides/reference/native-event-collision-with-emits.md +162 -0
- package/skills/vue-debug-guides/reference/no-passive-with-prevent.md +141 -0
- package/skills/vue-debug-guides/reference/no-v-if-with-v-for.md +136 -0
- package/skills/vue-debug-guides/reference/perf-computed-object-stability.md +157 -0
- package/skills/vue-debug-guides/reference/perf-props-stability-update-optimization.md +140 -0
- package/skills/vue-debug-guides/reference/plugin-global-properties-sparingly.md +109 -0
- package/skills/vue-debug-guides/reference/plugin-install-before-mount.md +124 -0
- package/skills/vue-debug-guides/reference/plugin-prefer-provide-inject-over-global-properties.md +120 -0
- package/skills/vue-debug-guides/reference/plugin-typescript-type-augmentation.md +157 -0
- package/skills/vue-debug-guides/reference/prop-defineprops-scope-limitation.md +161 -0
- package/skills/vue-debug-guides/reference/provide-inject-debugging-challenges.md +203 -0
- package/skills/vue-debug-guides/reference/provide-inject-default-value-factory.md +244 -0
- package/skills/vue-debug-guides/reference/provide-inject-reactivity-not-automatic.md +226 -0
- package/skills/vue-debug-guides/reference/provide-inject-synchronous-setup.md +235 -0
- package/skills/vue-debug-guides/reference/reactive-destructuring.md +89 -0
- package/skills/vue-debug-guides/reference/reactivity-debugging-hooks.md +132 -0
- package/skills/vue-debug-guides/reference/reactivity-markraw-for-non-reactive.md +149 -0
- package/skills/vue-debug-guides/reference/reactivity-proxy-identity-hazard.md +96 -0
- package/skills/vue-debug-guides/reference/reactivity-same-tick-batching.md +166 -0
- package/skills/vue-debug-guides/reference/ref-value-access.md +61 -0
- package/skills/vue-debug-guides/reference/refs-in-collections-need-value.md +81 -0
- package/skills/vue-debug-guides/reference/render-function-avoid-internal-vnode-properties.md +151 -0
- package/skills/vue-debug-guides/reference/render-function-vnodes-must-be-unique.md +133 -0
- package/skills/vue-debug-guides/reference/rendering-render-function-h-import-vue3.md +148 -0
- package/skills/vue-debug-guides/reference/rendering-render-function-return-from-setup.md +148 -0
- package/skills/vue-debug-guides/reference/rendering-render-function-slots-as-functions.md +168 -0
- package/skills/vue-debug-guides/reference/rendering-resolve-component-for-string-names.md +231 -0
- package/skills/vue-debug-guides/reference/select-initial-value-ios-bug.md +91 -0
- package/skills/vue-debug-guides/reference/self-referencing-component-name.md +157 -0
- package/skills/vue-debug-guides/reference/sfc-named-exports-forbidden.md +184 -0
- package/skills/vue-debug-guides/reference/sfc-scoped-css-child-component-styling.md +156 -0
- package/skills/vue-debug-guides/reference/sfc-scoped-css-dynamic-content.md +193 -0
- package/skills/vue-debug-guides/reference/sfc-scoped-css-slot-content.md +242 -0
- package/skills/vue-debug-guides/reference/sfc-script-setup-reactivity.md +195 -0
- package/skills/vue-debug-guides/reference/slot-forwarding-to-child-components.md +143 -0
- package/skills/vue-debug-guides/reference/slot-implicit-default-content.md +155 -0
- package/skills/vue-debug-guides/reference/slot-name-reserved-prop.md +109 -0
- package/skills/vue-debug-guides/reference/slot-named-scoped-explicit-default.md +95 -0
- package/skills/vue-debug-guides/reference/slot-render-scope-parent-only.md +135 -0
- package/skills/vue-debug-guides/reference/slot-v-slot-on-components-or-templates-only.md +122 -0
- package/skills/vue-debug-guides/reference/ssr-hydration-mismatch-causes.md +280 -0
- package/skills/vue-debug-guides/reference/ssr-platform-specific-apis.md +256 -0
- package/skills/vue-debug-guides/reference/state-ssr-cross-request-pollution.md +276 -0
- package/skills/vue-debug-guides/reference/suspense-no-builtin-error-handling.md +127 -0
- package/skills/vue-debug-guides/reference/suspense-ssr-hydration-issues.md +159 -0
- package/skills/vue-debug-guides/reference/tailwind-dynamic-class-generation.md +144 -0
- package/skills/vue-debug-guides/reference/teleport-scoped-styles-limitation.md +191 -0
- package/skills/vue-debug-guides/reference/teleport-ssr-hydration.md +152 -0
- package/skills/vue-debug-guides/reference/teleport-target-must-exist.md +113 -0
- package/skills/vue-debug-guides/reference/template-expressions-restrictions.md +114 -0
- package/skills/vue-debug-guides/reference/template-functions-no-side-effects.md +187 -0
- package/skills/vue-debug-guides/reference/template-ref-null-with-v-if.md +123 -0
- package/skills/vue-debug-guides/reference/template-ref-unwrapping-top-level.md +104 -0
- package/skills/vue-debug-guides/reference/template-ref-v-for-order.md +172 -0
- package/skills/vue-debug-guides/reference/textarea-no-interpolation.md +72 -0
- package/skills/vue-debug-guides/reference/transition-group-flip-inline-elements.md +152 -0
- package/skills/vue-debug-guides/reference/transition-group-move-animation-position-absolute.md +130 -0
- package/skills/vue-debug-guides/reference/transition-group-no-default-wrapper-vue3.md +152 -0
- package/skills/vue-debug-guides/reference/transition-js-hooks-done-callback.md +251 -0
- package/skills/vue-debug-guides/reference/transition-nested-duration.md +182 -0
- package/skills/vue-debug-guides/reference/transition-reusable-scoped-style.md +245 -0
- package/skills/vue-debug-guides/reference/transition-router-view-appear.md +193 -0
- package/skills/vue-debug-guides/reference/transition-type-when-mixed.md +172 -0
- package/skills/vue-debug-guides/reference/transition-unmount-hook-timing.md +149 -0
- package/skills/vue-debug-guides/reference/ts-defineprops-boolean-default-false.md +225 -0
- package/skills/vue-debug-guides/reference/ts-defineprops-imported-types-limitations.md +281 -0
- package/skills/vue-debug-guides/reference/ts-event-handler-explicit-typing.md +213 -0
- package/skills/vue-debug-guides/reference/ts-reactive-no-generic-argument.md +196 -0
- package/skills/vue-debug-guides/reference/ts-shallowref-for-dynamic-components.md +218 -0
- package/skills/vue-debug-guides/reference/ts-template-ref-null-handling.md +249 -0
- package/skills/vue-debug-guides/reference/ts-template-type-casting.md +214 -0
- package/skills/vue-debug-guides/reference/ts-withdefaults-mutable-factory-function.md +171 -0
- package/skills/vue-debug-guides/reference/undeclared-emits-double-firing.md +195 -0
- package/skills/vue-debug-guides/reference/use-template-ref-vue35.md +158 -0
- package/skills/vue-debug-guides/reference/v-else-must-follow-v-if.md +136 -0
- package/skills/vue-debug-guides/reference/v-for-component-props.md +95 -0
- package/skills/vue-debug-guides/reference/v-for-computed-reverse-sort.md +86 -0
- package/skills/vue-debug-guides/reference/v-for-key-attribute.md +90 -0
- package/skills/vue-debug-guides/reference/v-for-range-starts-at-one.md +66 -0
- package/skills/vue-debug-guides/reference/v-if-null-check-order.md +171 -0
- package/skills/vue-debug-guides/reference/v-model-ignores-html-attributes.md +83 -0
- package/skills/vue-debug-guides/reference/v-model-ime-composition.md +83 -0
- package/skills/vue-debug-guides/reference/v-model-number-modifier-behavior.md +124 -0
- package/skills/vue-debug-guides/reference/v-show-template-limitation.md +124 -0
- package/skills/vue-debug-guides/reference/watch-async-cleanup.md +180 -0
- package/skills/vue-debug-guides/reference/watch-async-creation-memory-leak.md +176 -0
- package/skills/vue-debug-guides/reference/watch-deep-same-object-reference.md +165 -0
- package/skills/vue-debug-guides/reference/watch-flush-timing.md +189 -0
- package/skills/vue-debug-guides/reference/watch-reactive-property-getter.md +108 -0
- package/skills/vue-debug-guides/reference/watcheffect-async-dependency-tracking.md +173 -0
- package/skills/vue-debug-guides/reference/watcheffect-flush-post-for-refs.md +176 -0
- package/skills/vue-jsx-best-practices/SKILL.md +12 -0
- package/skills/vue-jsx-best-practices/reference/render-function-jsx-vue-vs-react.md +141 -0
- package/skills/vue-options-api-best-practices/SKILL.md +23 -0
- package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-lifecycle-hooks.md +95 -0
- package/skills/vue-options-api-best-practices/reference/no-arrow-functions-in-methods.md +68 -0
- package/skills/vue-options-api-best-practices/reference/stateful-methods-lifecycle.md +61 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-arrow-functions-validators.md +141 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-computed-return-types.md +192 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-proptype-complex-types.md +212 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-provide-inject-limitations.md +135 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-type-event-handlers.md +202 -0
- package/skills/vue-options-api-best-practices/reference/ts-options-api-use-definecomponent.md +172 -0
- package/skills/vue-options-api-best-practices/reference/ts-strict-mode-options-api.md +197 -0
- package/skills/vue-pinia-best-practices/SKILL.md +21 -0
- package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
- package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
- package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
- package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
- package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
- package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
- package/skills/vue-router-best-practices/SKILL.md +23 -0
- package/skills/vue-router-best-practices/reference/router-beforeenter-no-param-trigger.md +167 -0
- package/skills/vue-router-best-practices/reference/router-beforerouteenter-no-this.md +176 -0
- package/skills/vue-router-best-practices/reference/router-guard-async-await-pattern.md +227 -0
- package/skills/vue-router-best-practices/reference/router-navigation-guard-infinite-loop.md +187 -0
- package/skills/vue-router-best-practices/reference/router-navigation-guard-next-deprecated.md +150 -0
- package/skills/vue-router-best-practices/reference/router-param-change-no-lifecycle.md +181 -0
- package/skills/vue-router-best-practices/reference/router-simple-routing-cleanup.md +209 -0
- package/skills/vue-router-best-practices/reference/router-use-vue-router-for-production.md +183 -0
- package/skills/vue-testing-best-practices/SKILL.md +29 -0
- package/skills/vue-testing-best-practices/reference/async-component-testing.md +163 -0
- package/skills/vue-testing-best-practices/reference/teleport-testing-complexity.md +158 -0
- package/skills/vue-testing-best-practices/reference/testing-async-await-flushpromises.md +175 -0
- package/skills/vue-testing-best-practices/reference/testing-browser-vs-node-runners.md +208 -0
- package/skills/vue-testing-best-practices/reference/testing-component-blackbox-approach.md +144 -0
- package/skills/vue-testing-best-practices/reference/testing-composables-helper-wrapper.md +238 -0
- package/skills/vue-testing-best-practices/reference/testing-e2e-playwright-recommended.md +242 -0
- package/skills/vue-testing-best-practices/reference/testing-no-snapshot-only.md +197 -0
- package/skills/vue-testing-best-practices/reference/testing-pinia-store-setup.md +228 -0
- package/skills/vue-testing-best-practices/reference/testing-suspense-async-components.md +229 -0
- 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)
|