@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,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Wrap Mutable Default Values in Factory Functions
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Without factory functions, all component instances share the same mutable reference causing cross-contamination bugs
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, typescript, props, withDefaults, mutable-types]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Wrap Mutable Default Values in Factory Functions
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - When using `withDefaults()` with type-based props declaration, default values for mutable types (arrays and objects) MUST be wrapped in factory functions. Without this, all component instances share the same reference, causing bugs where modifying the prop in one instance affects all others.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Always wrap array defaults in factory functions: `() => []`
|
|
16
|
+
- [ ] Always wrap object defaults in factory functions: `() => ({})`
|
|
17
|
+
- [ ] Primitive types (string, number, boolean) do NOT need factory functions
|
|
18
|
+
- [ ] Review existing components for this pattern
|
|
19
|
+
|
|
20
|
+
## The Problem: Shared Mutable References
|
|
21
|
+
|
|
22
|
+
**WRONG - Shared reference across instances:**
|
|
23
|
+
```vue
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
interface Props {
|
|
26
|
+
items?: string[]
|
|
27
|
+
config?: { theme: string }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
31
|
+
items: ['default'], // WRONG! All instances share this array
|
|
32
|
+
config: { theme: 'light' } // WRONG! All instances share this object
|
|
33
|
+
})
|
|
34
|
+
</script>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
When you have multiple instances of this component:
|
|
38
|
+
```vue
|
|
39
|
+
<template>
|
|
40
|
+
<!-- Both share the SAME items array! -->
|
|
41
|
+
<MyComponent ref="comp1" />
|
|
42
|
+
<MyComponent ref="comp2" />
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<script setup>
|
|
46
|
+
// If comp1 modifies its items, comp2's items change too!
|
|
47
|
+
comp1.value.items.push('new item') // comp2 also has 'new item' now
|
|
48
|
+
</script>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## The Solution: Factory Functions
|
|
52
|
+
|
|
53
|
+
**CORRECT - Unique instance per component:**
|
|
54
|
+
```vue
|
|
55
|
+
<script setup lang="ts">
|
|
56
|
+
interface Props {
|
|
57
|
+
items?: string[]
|
|
58
|
+
config?: { theme: string }
|
|
59
|
+
nested?: { data: { values: number[] } }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
63
|
+
items: () => ['default'], // Factory function!
|
|
64
|
+
config: () => ({ theme: 'light' }), // Factory function!
|
|
65
|
+
nested: () => ({ data: { values: [] } }) // Factory function!
|
|
66
|
+
})
|
|
67
|
+
</script>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## When Factory Functions Are Required
|
|
71
|
+
|
|
72
|
+
| Type | Factory Required | Example Default |
|
|
73
|
+
|------|-----------------|-----------------|
|
|
74
|
+
| `string` | No | `'hello'` |
|
|
75
|
+
| `number` | No | `42` |
|
|
76
|
+
| `boolean` | No | `false` |
|
|
77
|
+
| `string[]` | **Yes** | `() => []` |
|
|
78
|
+
| `number[]` | **Yes** | `() => [1, 2, 3]` |
|
|
79
|
+
| `object` | **Yes** | `() => ({})` |
|
|
80
|
+
| `Map` | **Yes** | `() => new Map()` |
|
|
81
|
+
| `Set` | **Yes** | `() => new Set()` |
|
|
82
|
+
| `Date` | **Yes** | `() => new Date()` |
|
|
83
|
+
|
|
84
|
+
## Complete Example
|
|
85
|
+
|
|
86
|
+
```vue
|
|
87
|
+
<script setup lang="ts">
|
|
88
|
+
interface User {
|
|
89
|
+
id: string
|
|
90
|
+
name: string
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interface Props {
|
|
94
|
+
// Primitives - no factory needed
|
|
95
|
+
title?: string
|
|
96
|
+
count?: number
|
|
97
|
+
disabled?: boolean
|
|
98
|
+
|
|
99
|
+
// Mutable types - factory required
|
|
100
|
+
items?: string[]
|
|
101
|
+
users?: User[]
|
|
102
|
+
metadata?: Record<string, unknown>
|
|
103
|
+
selectedIds?: Set<string>
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
107
|
+
// Primitives
|
|
108
|
+
title: 'Default Title',
|
|
109
|
+
count: 0,
|
|
110
|
+
disabled: false,
|
|
111
|
+
|
|
112
|
+
// Mutable types with factory functions
|
|
113
|
+
items: () => [],
|
|
114
|
+
users: () => [],
|
|
115
|
+
metadata: () => ({}),
|
|
116
|
+
selectedIds: () => new Set()
|
|
117
|
+
})
|
|
118
|
+
</script>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Vue 3.5+ Reactive Props Destructure
|
|
122
|
+
|
|
123
|
+
Vue 3.5 introduces reactive props destructure, which handles this automatically:
|
|
124
|
+
|
|
125
|
+
```vue
|
|
126
|
+
<script setup lang="ts">
|
|
127
|
+
interface Props {
|
|
128
|
+
items?: string[]
|
|
129
|
+
config?: { theme: string }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Vue 3.5+ - defaults work correctly without explicit factory functions
|
|
133
|
+
const {
|
|
134
|
+
items = ['default'], // Each instance gets its own array
|
|
135
|
+
config = { theme: 'light' } // Each instance gets its own object
|
|
136
|
+
} = defineProps<Props>()
|
|
137
|
+
</script>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Note: Under the hood, Vue 3.5 handles the instance isolation for you.
|
|
141
|
+
|
|
142
|
+
## Common Bug Pattern
|
|
143
|
+
|
|
144
|
+
This bug often appears in list/table components:
|
|
145
|
+
|
|
146
|
+
```vue
|
|
147
|
+
<!-- ListItem.vue - BUGGY -->
|
|
148
|
+
<script setup lang="ts">
|
|
149
|
+
interface Props {
|
|
150
|
+
selectedRows?: number[]
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// All ListItems share the same selectedRows array!
|
|
154
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
155
|
+
selectedRows: [] // BUG: Missing factory function
|
|
156
|
+
})
|
|
157
|
+
</script>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Users report: "Selecting a row in one table selects it in all tables!"
|
|
161
|
+
|
|
162
|
+
**Fix:**
|
|
163
|
+
```typescript
|
|
164
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
165
|
+
selectedRows: () => [] // Now each instance has its own array
|
|
166
|
+
})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Reference
|
|
170
|
+
- [Vue.js TypeScript with Composition API - Default Props](https://vuejs.org/guide/typescript/composition-api.html#props-default-values)
|
|
171
|
+
- [Vue RFC - Reactive Props Destructure](https://github.com/vuejs/rfcs/discussions/502)
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Undeclared Emits Cause Double Event Firing
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Native events re-emitted without declaration fire twice - once from emit() and once from native listener on root element
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, emits, defineEmits, events, native-events, fallthrough]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Undeclared Emits Cause Double Event Firing
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - When a component re-emits a native DOM event (like `click`) without declaring it in `emits`, the event can fire twice. This happens because undeclared event listeners become part of `$attrs` and fall through to the root element, where they listen for native events while your `emit()` call also fires.
|
|
12
|
+
|
|
13
|
+
This is a common bug when wrapping native elements or migrating from Vue 2 to Vue 3.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Declare all emitted events in `defineEmits()` or `emits` option
|
|
18
|
+
- [ ] Pay special attention when re-emitting native events (click, input, change, etc.)
|
|
19
|
+
- [ ] Check for double firing if component wraps native elements
|
|
20
|
+
- [ ] Understand that undeclared listeners fall through to `$attrs`
|
|
21
|
+
|
|
22
|
+
## The Problem
|
|
23
|
+
|
|
24
|
+
**Incorrect - Undeclared emit causes double firing:**
|
|
25
|
+
```vue
|
|
26
|
+
<!-- MyButton.vue -->
|
|
27
|
+
<script setup>
|
|
28
|
+
// NO defineEmits declaration!
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<!-- Native click listener from parent falls through to button -->
|
|
33
|
+
<!-- PLUS we re-emit click -->
|
|
34
|
+
<button @click="$emit('click', $event)">
|
|
35
|
+
<slot></slot>
|
|
36
|
+
</button>
|
|
37
|
+
</template>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```vue
|
|
41
|
+
<!-- Parent.vue -->
|
|
42
|
+
<template>
|
|
43
|
+
<!-- This handler fires TWICE on each click! -->
|
|
44
|
+
<MyButton @click="handleClick">Click me</MyButton>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<script setup>
|
|
48
|
+
function handleClick() {
|
|
49
|
+
console.log('clicked') // Logs twice!
|
|
50
|
+
}
|
|
51
|
+
</script>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**What happens:**
|
|
55
|
+
1. User clicks the button
|
|
56
|
+
2. Native click event fires on the button element
|
|
57
|
+
3. `@click` falls through to button (because 'click' isn't in emits), triggering `handleClick`
|
|
58
|
+
4. The button's `@click="$emit('click', $event)"` also fires, emitting a component event
|
|
59
|
+
5. Parent's `@click="handleClick"` receives the emitted event, triggering `handleClick` again
|
|
60
|
+
|
|
61
|
+
## The Solution
|
|
62
|
+
|
|
63
|
+
**Correct - Declare the emit:**
|
|
64
|
+
```vue
|
|
65
|
+
<!-- MyButton.vue -->
|
|
66
|
+
<script setup>
|
|
67
|
+
// Declare 'click' as a component event
|
|
68
|
+
const emit = defineEmits(['click'])
|
|
69
|
+
</script>
|
|
70
|
+
|
|
71
|
+
<template>
|
|
72
|
+
<!-- Now @click="handleClick" in parent only listens to emit() -->
|
|
73
|
+
<button @click="emit('click', $event)">
|
|
74
|
+
<slot></slot>
|
|
75
|
+
</button>
|
|
76
|
+
</template>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```vue
|
|
80
|
+
<!-- Parent.vue -->
|
|
81
|
+
<template>
|
|
82
|
+
<!-- Now fires only once -->
|
|
83
|
+
<MyButton @click="handleClick">Click me</MyButton>
|
|
84
|
+
</template>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
When you declare `click` in `emits`:
|
|
88
|
+
- Vue knows `@click` on the component is listening for a component event
|
|
89
|
+
- The listener does NOT fall through to the root element
|
|
90
|
+
- Only your explicit `emit('click')` triggers the parent's handler
|
|
91
|
+
|
|
92
|
+
## Options API Version
|
|
93
|
+
|
|
94
|
+
**Correct - Using emits option:**
|
|
95
|
+
```vue
|
|
96
|
+
<script>
|
|
97
|
+
export default {
|
|
98
|
+
emits: ['click', 'input', 'change'],
|
|
99
|
+
|
|
100
|
+
methods: {
|
|
101
|
+
handleClick(event) {
|
|
102
|
+
this.$emit('click', event)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
</script>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Common Scenarios
|
|
110
|
+
|
|
111
|
+
### Wrapping Form Inputs
|
|
112
|
+
|
|
113
|
+
```vue
|
|
114
|
+
<!-- CustomInput.vue -->
|
|
115
|
+
<script setup>
|
|
116
|
+
// Declare all events you re-emit
|
|
117
|
+
const emit = defineEmits(['input', 'change', 'focus', 'blur'])
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<template>
|
|
121
|
+
<input
|
|
122
|
+
@input="emit('input', $event)"
|
|
123
|
+
@change="emit('change', $event)"
|
|
124
|
+
@focus="emit('focus', $event)"
|
|
125
|
+
@blur="emit('blur', $event)"
|
|
126
|
+
/>
|
|
127
|
+
</template>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Wrapper Components
|
|
131
|
+
|
|
132
|
+
```vue
|
|
133
|
+
<!-- IconButton.vue -->
|
|
134
|
+
<script setup>
|
|
135
|
+
const emit = defineEmits(['click'])
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
<template>
|
|
139
|
+
<button @click="emit('click', $event)">
|
|
140
|
+
<Icon :name="icon" />
|
|
141
|
+
<slot></slot>
|
|
142
|
+
</button>
|
|
143
|
+
</template>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Alternative: Don't Re-emit, Let It Fall Through
|
|
147
|
+
|
|
148
|
+
If your component has a single root element and you want native event behavior:
|
|
149
|
+
|
|
150
|
+
```vue
|
|
151
|
+
<!-- MyButton.vue -->
|
|
152
|
+
<script setup>
|
|
153
|
+
// Don't declare 'click' - let it fall through naturally
|
|
154
|
+
const emit = defineEmits(['custom-action'])
|
|
155
|
+
</script>
|
|
156
|
+
|
|
157
|
+
<template>
|
|
158
|
+
<!-- Native @click from parent falls through to this button -->
|
|
159
|
+
<button>
|
|
160
|
+
<slot></slot>
|
|
161
|
+
</button>
|
|
162
|
+
</template>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```vue
|
|
166
|
+
<!-- Parent.vue -->
|
|
167
|
+
<template>
|
|
168
|
+
<!-- This native click falls through to the button -->
|
|
169
|
+
<MyButton @click="handleClick">Click me</MyButton>
|
|
170
|
+
</template>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
This works because:
|
|
174
|
+
- You don't re-emit 'click' explicitly
|
|
175
|
+
- The native click listener falls through to the single root button
|
|
176
|
+
- Native click fires once when button is clicked
|
|
177
|
+
|
|
178
|
+
## Debugging Double Firing
|
|
179
|
+
|
|
180
|
+
```vue
|
|
181
|
+
<script setup>
|
|
182
|
+
function handleClick(event) {
|
|
183
|
+
console.log('Event type:', event?.type)
|
|
184
|
+
console.log('Is native:', event instanceof Event)
|
|
185
|
+
console.trace('Click handler called')
|
|
186
|
+
}
|
|
187
|
+
</script>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
If you see two stack traces with different origins, you have the double-firing issue.
|
|
191
|
+
|
|
192
|
+
## Reference
|
|
193
|
+
- [Vue 3 Migration - emits Option](https://v3-migration.vuejs.org/breaking-changes/emits-option)
|
|
194
|
+
- [Vue.js Component Events](https://vuejs.org/guide/components/events.html)
|
|
195
|
+
- [Vue.js Fallthrough Attributes](https://vuejs.org/guide/components/attrs.html)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use useTemplateRef for Template Refs in Vue 3.5+
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Legacy ref pattern is error-prone due to name matching requirement
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, vue35, template-refs, useTemplateRef, composition-api]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Use useTemplateRef for Template Refs in Vue 3.5+
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Before Vue 3.5, template refs required declaring a `ref()` with a name exactly matching the template's ref attribute. This fragile connection breaks silently during refactoring. Vue 3.5's `useTemplateRef()` eliminates this issue with explicit binding and better TypeScript support.
|
|
12
|
+
|
|
13
|
+
The legacy pattern causes no errors or warnings when names don't match - the ref simply stays null, leading to confusing debugging sessions.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Use `useTemplateRef('ref-name')` in Vue 3.5+ projects
|
|
18
|
+
- [ ] The first argument must exactly match the ref attribute value in the template
|
|
19
|
+
- [ ] IDE support provides auto-completion for available ref names
|
|
20
|
+
- [ ] TypeScript automatically infers the element type
|
|
21
|
+
|
|
22
|
+
**Incorrect (Legacy Pattern):**
|
|
23
|
+
```vue
|
|
24
|
+
<script setup>
|
|
25
|
+
import { ref, onMounted } from 'vue'
|
|
26
|
+
|
|
27
|
+
// FRAGILE: Variable name MUST match template ref value exactly
|
|
28
|
+
const input = ref(null)
|
|
29
|
+
|
|
30
|
+
// DANGER: After refactoring, names may not match
|
|
31
|
+
const inputElement = ref(null) // Renamed variable...
|
|
32
|
+
|
|
33
|
+
onMounted(() => {
|
|
34
|
+
// NO ERROR - just silently null!
|
|
35
|
+
inputElement.value?.focus() // Does nothing
|
|
36
|
+
})
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<!-- But template still uses old name -->
|
|
41
|
+
<input ref="input" />
|
|
42
|
+
</template>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```vue
|
|
46
|
+
<script setup>
|
|
47
|
+
import { ref } from 'vue'
|
|
48
|
+
|
|
49
|
+
// TYPO: 'inupt' instead of 'input' - no warning!
|
|
50
|
+
const inupt = ref(null)
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<template>
|
|
54
|
+
<input ref="input" />
|
|
55
|
+
<!-- inupt.value will always be null -->
|
|
56
|
+
</template>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Correct (Vue 3.5+):**
|
|
60
|
+
```vue
|
|
61
|
+
<script setup>
|
|
62
|
+
import { useTemplateRef, onMounted } from 'vue'
|
|
63
|
+
|
|
64
|
+
// CORRECT: Explicit binding - argument matches template ref
|
|
65
|
+
const inputElement = useTemplateRef('my-input')
|
|
66
|
+
|
|
67
|
+
onMounted(() => {
|
|
68
|
+
inputElement.value?.focus()
|
|
69
|
+
})
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<template>
|
|
73
|
+
<!-- ref name is explicitly connected via useTemplateRef argument -->
|
|
74
|
+
<input ref="my-input" />
|
|
75
|
+
</template>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```vue
|
|
79
|
+
<script setup>
|
|
80
|
+
import { useTemplateRef, onMounted } from 'vue'
|
|
81
|
+
|
|
82
|
+
// BENEFITS:
|
|
83
|
+
// 1. Variable name is independent of ref attribute
|
|
84
|
+
// 2. IDE auto-completes available ref names
|
|
85
|
+
// 3. TypeScript infers correct element type
|
|
86
|
+
// 4. Typos in argument cause visible errors
|
|
87
|
+
|
|
88
|
+
const searchInput = useTemplateRef('search-box')
|
|
89
|
+
const submitButton = useTemplateRef('submit-btn')
|
|
90
|
+
|
|
91
|
+
onMounted(() => {
|
|
92
|
+
// TypeScript knows these are HTMLInputElement and HTMLButtonElement
|
|
93
|
+
searchInput.value?.focus()
|
|
94
|
+
submitButton.value?.disabled = false
|
|
95
|
+
})
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<template>
|
|
99
|
+
<input ref="search-box" type="search" />
|
|
100
|
+
<button ref="submit-btn">Submit</button>
|
|
101
|
+
</template>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Limitation: v-for Refs
|
|
105
|
+
|
|
106
|
+
`useTemplateRef()` does **NOT** work with `v-for` refs. You must use the legacy `ref()` pattern for collecting multiple element references in a loop.
|
|
107
|
+
|
|
108
|
+
```vue
|
|
109
|
+
<script setup>
|
|
110
|
+
import { ref, onMounted } from 'vue'
|
|
111
|
+
|
|
112
|
+
// CORRECT: Legacy pattern required for v-for refs
|
|
113
|
+
const itemRefs = ref([])
|
|
114
|
+
|
|
115
|
+
onMounted(() => {
|
|
116
|
+
// itemRefs.value is an array of DOM elements
|
|
117
|
+
itemRefs.value.forEach(el => {
|
|
118
|
+
console.log(el.textContent)
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<template>
|
|
124
|
+
<ul>
|
|
125
|
+
<li v-for="item in items" ref="itemRefs" :key="item.id">
|
|
126
|
+
{{ item.text }}
|
|
127
|
+
</li>
|
|
128
|
+
</ul>
|
|
129
|
+
</template>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Why this limitation exists:** When using `v-for`, Vue populates the ref with an array of elements. The `useTemplateRef()` API was designed for single element references and does not support the array population mechanism that `v-for` requires.
|
|
133
|
+
|
|
134
|
+
## Migration Guide
|
|
135
|
+
|
|
136
|
+
```vue
|
|
137
|
+
<!-- BEFORE (Vue < 3.5) -->
|
|
138
|
+
<script setup>
|
|
139
|
+
import { ref } from 'vue'
|
|
140
|
+
const myElement = ref(null) // Name must match template
|
|
141
|
+
</script>
|
|
142
|
+
<template>
|
|
143
|
+
<div ref="myElement"></div>
|
|
144
|
+
</template>
|
|
145
|
+
|
|
146
|
+
<!-- AFTER (Vue 3.5+) -->
|
|
147
|
+
<script setup>
|
|
148
|
+
import { useTemplateRef } from 'vue'
|
|
149
|
+
const element = useTemplateRef('my-element') // Any variable name
|
|
150
|
+
</script>
|
|
151
|
+
<template>
|
|
152
|
+
<div ref="my-element"></div>
|
|
153
|
+
</template>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Reference
|
|
157
|
+
- [Vue.js Template Refs - Composition API](https://vuejs.org/guide/essentials/template-refs.html#accessing-the-refs)
|
|
158
|
+
- [Vue 3.5 Release Notes](https://blog.vuejs.org/posts/vue-3-5)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: v-else Must Immediately Follow v-if or v-else-if
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Misplaced v-else causes a compile-time error in SFCs, or renders unconditionally with runtime template compilation
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, conditional-rendering, v-if, v-else, v-else-if]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# v-else Must Immediately Follow v-if or v-else-if
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - A `v-else` or `v-else-if` element must immediately follow a `v-if` or `v-else-if` element in the DOM. If there's any element in between:
|
|
12
|
+
|
|
13
|
+
- **SFC compilation (Vite/vue-loader):** Vue throws a **compile-time SyntaxError** - the code won't build at all
|
|
14
|
+
- **Runtime template compilation:** The element renders unconditionally (always visible), losing its conditional behavior
|
|
15
|
+
|
|
16
|
+
In most modern Vue projects using Single File Components, this is caught at build time.
|
|
17
|
+
|
|
18
|
+
## Task Checklist
|
|
19
|
+
|
|
20
|
+
- [ ] Place v-else immediately after the v-if element (no elements in between)
|
|
21
|
+
- [ ] Place v-else-if immediately after v-if or another v-else-if
|
|
22
|
+
- [ ] Use `<template>` wrapper if you need to group multiple elements within a branch
|
|
23
|
+
- [ ] If you need content between conditions, restructure using nested conditionals or computed
|
|
24
|
+
|
|
25
|
+
**Incorrect:**
|
|
26
|
+
```html
|
|
27
|
+
<!-- WRONG: v-else not immediately after v-if -->
|
|
28
|
+
<template>
|
|
29
|
+
<div v-if="isLoggedIn">
|
|
30
|
+
Welcome back!
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<p>Some other content in between</p>
|
|
34
|
+
|
|
35
|
+
<div v-else>
|
|
36
|
+
<!-- This v-else is NOT recognized! It will ALWAYS render (unconditionally) -->
|
|
37
|
+
Please log in
|
|
38
|
+
</div>
|
|
39
|
+
</template>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<!-- WRONG: Comment or whitespace element between -->
|
|
44
|
+
<template>
|
|
45
|
+
<span v-if="status === 'loading'">Loading...</span>
|
|
46
|
+
<!-- This comment breaks the chain! -->
|
|
47
|
+
<span v-else-if="status === 'error'">Error occurred</span>
|
|
48
|
+
<span v-else>Done</span>
|
|
49
|
+
</template>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<!-- WRONG: Text node between conditions -->
|
|
54
|
+
<template>
|
|
55
|
+
<div v-if="showA">A</div>
|
|
56
|
+
Just some text here
|
|
57
|
+
<div v-else>B</div> <!-- Not recognized, always renders -->
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Correct:**
|
|
62
|
+
```html
|
|
63
|
+
<!-- CORRECT: v-else immediately follows v-if -->
|
|
64
|
+
<template>
|
|
65
|
+
<div v-if="isLoggedIn">
|
|
66
|
+
Welcome back!
|
|
67
|
+
</div>
|
|
68
|
+
<div v-else>
|
|
69
|
+
Please log in
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<p>Some other content (placed after the conditional block)</p>
|
|
73
|
+
</template>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```html
|
|
77
|
+
<!-- CORRECT: Full v-if/v-else-if/v-else chain -->
|
|
78
|
+
<template>
|
|
79
|
+
<span v-if="status === 'loading'">Loading...</span>
|
|
80
|
+
<span v-else-if="status === 'error'">Error: {{ errorMessage }}</span>
|
|
81
|
+
<span v-else-if="status === 'empty'">No data found</span>
|
|
82
|
+
<span v-else>{{ data }}</span>
|
|
83
|
+
</template>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<!-- CORRECT: Using <template> for multiple elements per branch -->
|
|
88
|
+
<template>
|
|
89
|
+
<template v-if="isLoggedIn">
|
|
90
|
+
<h1>Welcome back!</h1>
|
|
91
|
+
<p>Your dashboard</p>
|
|
92
|
+
<UserStats />
|
|
93
|
+
</template>
|
|
94
|
+
<template v-else>
|
|
95
|
+
<h1>Please log in</h1>
|
|
96
|
+
<LoginForm />
|
|
97
|
+
</template>
|
|
98
|
+
</template>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<!-- CORRECT: Restructure if you need content between conditions -->
|
|
103
|
+
<template>
|
|
104
|
+
<div class="conditional-section">
|
|
105
|
+
<div v-if="isLoggedIn">Welcome back!</div>
|
|
106
|
+
<div v-else>Please log in</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="always-shown">
|
|
110
|
+
Some other content
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div class="another-conditional">
|
|
114
|
+
<div v-if="showMore">More info</div>
|
|
115
|
+
<div v-else>Click to expand</div>
|
|
116
|
+
</div>
|
|
117
|
+
</template>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Debugging Tips
|
|
121
|
+
|
|
122
|
+
```html
|
|
123
|
+
<!-- If v-else is always visible (rendering unconditionally), check for: -->
|
|
124
|
+
|
|
125
|
+
<!-- 1. Elements between v-if and v-else -->
|
|
126
|
+
<!-- 2. HTML comments that break the chain -->
|
|
127
|
+
<!-- 3. Text nodes (including whitespace in some cases) -->
|
|
128
|
+
<!-- 4. Component tags that render content between -->
|
|
129
|
+
|
|
130
|
+
<!-- SFC compilation throws: "v-else/v-else-if has no adjacent v-if or v-else-if" (build fails) -->
|
|
131
|
+
<!-- Runtime template compilation shows a warning but renders the element unconditionally -->
|
|
132
|
+
<!-- Vue DevTools will show the v-else element as not part of the condition chain -->
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Reference
|
|
136
|
+
- [Vue.js Conditional Rendering - v-else](https://vuejs.org/guide/essentials/conditional.html#v-else)
|