@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,184 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Mounted and Unmounted Hooks Do Not Run During SSR
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: SSR applications may fail if mounted-only code is essential for functionality
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, lifecycle, ssr, server-side-rendering, nuxt, onMounted, mounted, hydration]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Mounted and Unmounted Hooks Do Not Run During SSR
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - During server-side rendering (SSR), lifecycle hooks like `mounted`, `onMounted`, `unmounted`, and `onUnmounted` are never called on the server. This can cause differences between server-rendered and client-rendered content, hydration mismatches, and missing functionality if critical logic is placed only in these hooks.
|
|
12
|
+
|
|
13
|
+
On the server, only `beforeCreate`, `created`, and their Composition API equivalents run. Client-specific operations (DOM access, browser APIs, third-party libraries) must be in mounted hooks, but you must handle the SSR case appropriately.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Place browser-specific code (window, document, localStorage) in mounted/onMounted
|
|
18
|
+
- [ ] Ensure critical data fetching happens in hooks that run on server (created)
|
|
19
|
+
- [ ] Handle hydration mismatches for content that differs client vs server
|
|
20
|
+
- [ ] Use `<ClientOnly>` wrapper (Nuxt) or conditional rendering for client-only components
|
|
21
|
+
- [ ] Check for browser environment before using browser APIs
|
|
22
|
+
|
|
23
|
+
**Incorrect:**
|
|
24
|
+
```javascript
|
|
25
|
+
// WRONG: Accessing browser APIs in created - breaks SSR
|
|
26
|
+
export default {
|
|
27
|
+
created() {
|
|
28
|
+
// These don't exist on the server!
|
|
29
|
+
this.width = window.innerWidth // ReferenceError: window is not defined
|
|
30
|
+
this.savedData = localStorage.getItem('data') // ReferenceError: localStorage is not defined
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
// WRONG: Critical initialization only in mounted - won't run on server
|
|
37
|
+
export default {
|
|
38
|
+
data() {
|
|
39
|
+
return { user: null }
|
|
40
|
+
},
|
|
41
|
+
async mounted() {
|
|
42
|
+
// This won't run on server - page renders without user data
|
|
43
|
+
// Then hydrates with user data - causes flash of content
|
|
44
|
+
this.user = await fetchCurrentUser()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Correct:**
|
|
50
|
+
```javascript
|
|
51
|
+
// CORRECT: Data fetching in created (runs on server), DOM in mounted
|
|
52
|
+
export default {
|
|
53
|
+
data() {
|
|
54
|
+
return {
|
|
55
|
+
user: null,
|
|
56
|
+
windowWidth: 0
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
async created() {
|
|
60
|
+
// This runs on both server and client
|
|
61
|
+
this.user = await fetchCurrentUser()
|
|
62
|
+
},
|
|
63
|
+
mounted() {
|
|
64
|
+
// Browser-specific code safely in mounted
|
|
65
|
+
this.windowWidth = window.innerWidth
|
|
66
|
+
window.addEventListener('resize', this.handleResize)
|
|
67
|
+
},
|
|
68
|
+
unmounted() {
|
|
69
|
+
window.removeEventListener('resize', this.handleResize)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```vue
|
|
75
|
+
<!-- CORRECT: Composition API with SSR awareness -->
|
|
76
|
+
<script setup>
|
|
77
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
78
|
+
|
|
79
|
+
const user = ref(null)
|
|
80
|
+
const windowWidth = ref(0)
|
|
81
|
+
|
|
82
|
+
// This runs on both server and client (during setup)
|
|
83
|
+
user.value = await useFetch('/api/user')
|
|
84
|
+
|
|
85
|
+
// These only run on client
|
|
86
|
+
onMounted(() => {
|
|
87
|
+
windowWidth.value = window.innerWidth
|
|
88
|
+
window.addEventListener('resize', handleResize)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
onUnmounted(() => {
|
|
92
|
+
window.removeEventListener('resize', handleResize)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
function handleResize() {
|
|
96
|
+
windowWidth.value = window.innerWidth
|
|
97
|
+
}
|
|
98
|
+
</script>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Checking for Browser Environment
|
|
102
|
+
|
|
103
|
+
```javascript
|
|
104
|
+
// CORRECT: Guard browser API access
|
|
105
|
+
export default {
|
|
106
|
+
data() {
|
|
107
|
+
return { theme: 'light' }
|
|
108
|
+
},
|
|
109
|
+
created() {
|
|
110
|
+
// Check if we're in browser before accessing browser APIs
|
|
111
|
+
if (typeof window !== 'undefined') {
|
|
112
|
+
this.theme = localStorage.getItem('theme') || 'light'
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
mounted() {
|
|
116
|
+
// mounted only runs in browser, so this is always safe
|
|
117
|
+
this.applyTheme()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Nuxt.js Specific Patterns
|
|
123
|
+
|
|
124
|
+
```vue
|
|
125
|
+
<!-- CORRECT: Using Nuxt's ClientOnly for client-specific components -->
|
|
126
|
+
<template>
|
|
127
|
+
<div>
|
|
128
|
+
<!-- This content renders on both server and client -->
|
|
129
|
+
<h1>Dashboard</h1>
|
|
130
|
+
|
|
131
|
+
<!-- This only renders on client - no hydration mismatch -->
|
|
132
|
+
<ClientOnly>
|
|
133
|
+
<ChartComponent :data="chartData" />
|
|
134
|
+
<template #fallback>
|
|
135
|
+
<p>Loading chart...</p>
|
|
136
|
+
</template>
|
|
137
|
+
</ClientOnly>
|
|
138
|
+
</div>
|
|
139
|
+
</template>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
// CORRECT: Using Nuxt's process.client/process.server
|
|
144
|
+
export default {
|
|
145
|
+
created() {
|
|
146
|
+
if (process.client) {
|
|
147
|
+
// Only runs in browser
|
|
148
|
+
this.initAnalytics()
|
|
149
|
+
}
|
|
150
|
+
if (process.server) {
|
|
151
|
+
// Only runs on server
|
|
152
|
+
this.logServerRequest()
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Handling Hydration Mismatches
|
|
159
|
+
|
|
160
|
+
```vue
|
|
161
|
+
<script setup>
|
|
162
|
+
import { ref, onMounted } from 'vue'
|
|
163
|
+
|
|
164
|
+
// Start with a value that matches what server renders
|
|
165
|
+
const currentTime = ref(null)
|
|
166
|
+
|
|
167
|
+
onMounted(() => {
|
|
168
|
+
// Update to real value only on client
|
|
169
|
+
// This prevents hydration mismatch
|
|
170
|
+
currentTime.value = new Date().toLocaleTimeString()
|
|
171
|
+
})
|
|
172
|
+
</script>
|
|
173
|
+
|
|
174
|
+
<template>
|
|
175
|
+
<!-- Renders null on server, then updates on client -->
|
|
176
|
+
<span v-if="currentTime">{{ currentTime }}</span>
|
|
177
|
+
<span v-else>Loading...</span>
|
|
178
|
+
</template>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Reference
|
|
182
|
+
- [Vue.js SSR Guide](https://vuejs.org/guide/scaling-up/ssr.html)
|
|
183
|
+
- [Nuxt.js Lifecycle](https://nuxt.com/docs/api/composables/use-nuxt-app#lifecycle-hooks)
|
|
184
|
+
- [Vue SSR Hydration](https://vuejs.org/guide/scaling-up/ssr.html#client-hydration)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Locally Registered Components Are Not Available in Descendants
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Common source of "component not found" errors in nested components
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, component-registration, local-registration, scope, nested-components]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Locally Registered Components Are Not Available in Descendants
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - Locally registered components are only available in the component where they are registered, NOT in its child or descendant components. This is a common source of "Unknown component" or "Failed to resolve component" errors when developers expect a component registered in a parent to be available in children.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Import and register components in every file where they are used
|
|
16
|
+
- [ ] Do not expect parent's local components to be available in children
|
|
17
|
+
- [ ] If a component is needed in many places, consider global registration only as a last resort
|
|
18
|
+
- [ ] Use IDE auto-import features to simplify repeated imports
|
|
19
|
+
|
|
20
|
+
**Incorrect:**
|
|
21
|
+
```vue
|
|
22
|
+
<!-- ParentComponent.vue -->
|
|
23
|
+
<script setup>
|
|
24
|
+
import Card from './Card.vue'
|
|
25
|
+
import ChildComponent from './ChildComponent.vue'
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<Card>Parent content</Card>
|
|
30
|
+
<ChildComponent />
|
|
31
|
+
</template>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```vue
|
|
35
|
+
<!-- ChildComponent.vue -->
|
|
36
|
+
<script setup>
|
|
37
|
+
// WRONG: Expecting Card to be available because parent imported it
|
|
38
|
+
// This will cause "Failed to resolve component: Card" error
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<template>
|
|
42
|
+
<!-- ERROR: Card is not available here! -->
|
|
43
|
+
<Card>
|
|
44
|
+
Child content
|
|
45
|
+
</Card>
|
|
46
|
+
</template>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Correct:**
|
|
50
|
+
```vue
|
|
51
|
+
<!-- ParentComponent.vue -->
|
|
52
|
+
<script setup>
|
|
53
|
+
import Card from './Card.vue'
|
|
54
|
+
import ChildComponent from './ChildComponent.vue'
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<template>
|
|
58
|
+
<Card>Parent content</Card>
|
|
59
|
+
<ChildComponent />
|
|
60
|
+
</template>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<!-- ChildComponent.vue -->
|
|
65
|
+
<script setup>
|
|
66
|
+
// CORRECT: Each component must import what it uses
|
|
67
|
+
import Card from './Card.vue'
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<template>
|
|
71
|
+
<Card>
|
|
72
|
+
Child content
|
|
73
|
+
</Card>
|
|
74
|
+
</template>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Common Scenarios
|
|
78
|
+
|
|
79
|
+
### Scenario 1: Deeply Nested Components
|
|
80
|
+
```vue
|
|
81
|
+
<!-- GrandchildComponent.vue -->
|
|
82
|
+
<script setup>
|
|
83
|
+
// Even if parent and grandparent both use Card,
|
|
84
|
+
// grandchild must import it separately
|
|
85
|
+
import Card from '@/components/Card.vue'
|
|
86
|
+
import Button from '@/components/Button.vue'
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<template>
|
|
90
|
+
<Card>
|
|
91
|
+
<Button>Click me</Button>
|
|
92
|
+
</Card>
|
|
93
|
+
</template>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Scenario 2: Slot Content with Components
|
|
97
|
+
```vue
|
|
98
|
+
<!-- Parent.vue -->
|
|
99
|
+
<script setup>
|
|
100
|
+
import Modal from './Modal.vue'
|
|
101
|
+
import Form from './Form.vue'
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<template>
|
|
105
|
+
<!-- Form is registered in Parent, so it works in slot content -->
|
|
106
|
+
<Modal>
|
|
107
|
+
<Form /> <!-- This works because slot content is compiled in Parent's scope -->
|
|
108
|
+
</Modal>
|
|
109
|
+
</template>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```vue
|
|
113
|
+
<!-- Modal.vue -->
|
|
114
|
+
<script setup>
|
|
115
|
+
// Modal doesn't need to import Form because slot content
|
|
116
|
+
// is compiled in the parent's scope, not Modal's scope
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<template>
|
|
120
|
+
<div class="modal">
|
|
121
|
+
<slot /> <!-- Form component works here because it's parent's slot content -->
|
|
122
|
+
</div>
|
|
123
|
+
</template>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Scenario 3: Dynamic Components
|
|
127
|
+
```vue
|
|
128
|
+
<!-- Container.vue -->
|
|
129
|
+
<script setup>
|
|
130
|
+
import TabA from './TabA.vue'
|
|
131
|
+
import TabB from './TabB.vue'
|
|
132
|
+
import { ref, shallowRef } from 'vue'
|
|
133
|
+
|
|
134
|
+
// When using dynamic components, all possible components must be imported
|
|
135
|
+
const currentTab = shallowRef(TabA)
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
<template>
|
|
139
|
+
<component :is="currentTab" />
|
|
140
|
+
</template>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Why This Design?
|
|
144
|
+
|
|
145
|
+
Local registration provides:
|
|
146
|
+
1. **Explicit dependencies** - You can see exactly what each component uses
|
|
147
|
+
2. **Tree-shaking** - Unused components are removed from bundles
|
|
148
|
+
3. **Clear scope** - No magic or implicit behavior
|
|
149
|
+
|
|
150
|
+
## Reference
|
|
151
|
+
- [Vue.js Component Registration - Local Registration](https://vuejs.org/guide/components/registration.html#local-registration)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: mount() Returns Component Instance, Not App Instance
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Using mount() return value for app configuration silently fails
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, createApp, mount, api]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# mount() Returns Component Instance, Not App Instance
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - The `.mount()` method returns the root component instance, not the application instance. Attempting to chain app configuration methods after mount() will fail or produce unexpected behavior.
|
|
12
|
+
|
|
13
|
+
This is a subtle API detail that catches developers who assume mount() returns the app for continued chaining.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Never chain app configuration methods after mount()
|
|
18
|
+
- [ ] If you need both instances, store them separately
|
|
19
|
+
- [ ] Use the component instance for accessing root component state or methods
|
|
20
|
+
- [ ] Use the app instance for configuration, plugins, and global registration
|
|
21
|
+
|
|
22
|
+
**Incorrect:**
|
|
23
|
+
```javascript
|
|
24
|
+
import { createApp } from 'vue'
|
|
25
|
+
import App from './App.vue'
|
|
26
|
+
|
|
27
|
+
// WRONG: Assuming mount returns app instance
|
|
28
|
+
const app = createApp(App).mount('#app')
|
|
29
|
+
|
|
30
|
+
// This fails! app is actually the root component instance
|
|
31
|
+
app.use(router) // TypeError: app.use is not a function
|
|
32
|
+
app.config.errorHandler = fn // app.config is undefined
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
// WRONG: Trying to save both in one line
|
|
37
|
+
const { app, component } = createApp(App).mount('#app') // Doesn't work this way
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Correct:**
|
|
41
|
+
```javascript
|
|
42
|
+
import { createApp } from 'vue'
|
|
43
|
+
import App from './App.vue'
|
|
44
|
+
|
|
45
|
+
// Store app instance separately
|
|
46
|
+
const app = createApp(App)
|
|
47
|
+
|
|
48
|
+
// Configure the app
|
|
49
|
+
app.use(router)
|
|
50
|
+
app.config.errorHandler = (err) => console.error(err)
|
|
51
|
+
|
|
52
|
+
// Store component instance if needed
|
|
53
|
+
const rootComponent = app.mount('#app')
|
|
54
|
+
|
|
55
|
+
// Now you have access to both:
|
|
56
|
+
// - app: the application instance (for config, plugins)
|
|
57
|
+
// - rootComponent: the root component instance (for state, methods)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
// If you only need the app configured and mounted (most common case):
|
|
62
|
+
createApp(App)
|
|
63
|
+
.use(router)
|
|
64
|
+
.use(pinia)
|
|
65
|
+
.mount('#app') // Return value (component instance) discarded - that's fine
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## When You Need the Root Component Instance
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
const app = createApp(App)
|
|
72
|
+
const vm = app.mount('#app')
|
|
73
|
+
|
|
74
|
+
// Access root component's exposed state/methods
|
|
75
|
+
console.log(vm.someExposedProperty)
|
|
76
|
+
vm.someExposedMethod()
|
|
77
|
+
|
|
78
|
+
// In Vue 3 with <script setup>, use defineExpose to expose:
|
|
79
|
+
// <script setup>
|
|
80
|
+
// import { ref } from 'vue'
|
|
81
|
+
// const count = ref(0)
|
|
82
|
+
// defineExpose({ count })
|
|
83
|
+
// </script>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Reference
|
|
87
|
+
- [Vue.js - Mounting the App](https://vuejs.org/guide/essentials/application.html#mounting-the-app)
|
|
88
|
+
- [Vue.js Application API - mount()](https://vuejs.org/api/application.html#app-mount)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Multi-Root Component Class Attribute Inheritance
|
|
2
|
+
|
|
3
|
+
## Rule
|
|
4
|
+
|
|
5
|
+
When a Vue 3 component has multiple root elements, class and style bindings from the parent will NOT automatically fall through. You must explicitly bind `$attrs.class` or `$attrs.style` to the target element.
|
|
6
|
+
|
|
7
|
+
## Why This Matters
|
|
8
|
+
|
|
9
|
+
- Vue 3 components can have multiple root elements (fragments)
|
|
10
|
+
- Unlike single-root components, multi-root components have no automatic attribute fallthrough
|
|
11
|
+
- Without explicit handling, classes and styles passed from parent are silently ignored
|
|
12
|
+
- Vue will emit a runtime warning, but styles/classes simply won't apply
|
|
13
|
+
|
|
14
|
+
## Bad Code
|
|
15
|
+
|
|
16
|
+
```vue
|
|
17
|
+
<!-- ChildComponent.vue - WRONG: classes from parent won't apply -->
|
|
18
|
+
<template>
|
|
19
|
+
<header>Header</header>
|
|
20
|
+
<main>Content</main>
|
|
21
|
+
<footer>Footer</footer>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<!-- Parent usage -->
|
|
25
|
+
<ChildComponent class="my-custom-class" />
|
|
26
|
+
<!-- Result: my-custom-class is NOT applied to any element -->
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Good Code
|
|
30
|
+
|
|
31
|
+
```vue
|
|
32
|
+
<!-- ChildComponent.vue - CORRECT: explicitly bind $attrs.class -->
|
|
33
|
+
<template>
|
|
34
|
+
<header>Header</header>
|
|
35
|
+
<main :class="$attrs.class" :style="$attrs.style">Content</main>
|
|
36
|
+
<footer>Footer</footer>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<!-- Or bind all attrs to one element -->
|
|
40
|
+
<template>
|
|
41
|
+
<header>Header</header>
|
|
42
|
+
<main v-bind="$attrs">Content</main>
|
|
43
|
+
<footer>Footer</footer>
|
|
44
|
+
</template>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Accessing $attrs in script setup
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<script setup>
|
|
51
|
+
import { useAttrs } from 'vue'
|
|
52
|
+
const attrs = useAttrs()
|
|
53
|
+
// attrs.class and attrs.style are available
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<template>
|
|
57
|
+
<header>Header</header>
|
|
58
|
+
<main :class="attrs.class">Content</main>
|
|
59
|
+
<footer>Footer</footer>
|
|
60
|
+
</template>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Disabling Automatic Inheritance
|
|
64
|
+
|
|
65
|
+
For single-root components where you want to control attribute placement:
|
|
66
|
+
|
|
67
|
+
```vue
|
|
68
|
+
<script>
|
|
69
|
+
export default {
|
|
70
|
+
inheritAttrs: false
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<script setup>
|
|
75
|
+
import { useAttrs } from 'vue'
|
|
76
|
+
const attrs = useAttrs()
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<template>
|
|
80
|
+
<div class="wrapper">
|
|
81
|
+
<input v-bind="attrs" />
|
|
82
|
+
</div>
|
|
83
|
+
</template>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Vue 2 to Vue 3 Migration Note
|
|
87
|
+
|
|
88
|
+
In Vue 2, `$attrs` did NOT include `class` and `style`. In Vue 3, `$attrs` contains ALL attributes including `class` and `style`. This is a breaking change that affects how you handle attribute forwarding.
|
|
89
|
+
|
|
90
|
+
## References
|
|
91
|
+
|
|
92
|
+
- [Fallthrough Attributes](https://vuejs.org/guide/components/attrs.html)
|
|
93
|
+
- [Vue 3 Migration Guide - $attrs includes class & style](https://v3-migration.vuejs.org/breaking-changes/attrs-includes-class-style)
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Declaring Native Event Names in Emits Blocks Native Listeners
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Declaring native events like 'click' in emits prevents native DOM event listeners from working
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, emits, native-events, click, event-collision]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Declaring Native Event Names in Emits Blocks Native Listeners
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - When you declare a native DOM event name (like `click`, `input`, `focus`) in your component's `emits` option, listeners for that event will ONLY respond to your component's `emit()` calls. They will no longer respond to the actual native DOM events on the root element.
|
|
12
|
+
|
|
13
|
+
This can cause unexpected behavior where clicks seem to stop working on your component.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Understand that declaring native event names changes listener behavior
|
|
18
|
+
- [ ] Always emit the event when you declare it
|
|
19
|
+
- [ ] Don't declare native events if you want fallthrough behavior
|
|
20
|
+
- [ ] Test click/input handling after adding emits declarations
|
|
21
|
+
|
|
22
|
+
## The Problem
|
|
23
|
+
|
|
24
|
+
**Incorrect - Declaring but not emitting:**
|
|
25
|
+
```vue
|
|
26
|
+
<!-- ClickableCard.vue -->
|
|
27
|
+
<script setup>
|
|
28
|
+
// Declared 'click' but never emit it!
|
|
29
|
+
const emit = defineEmits(['click', 'select'])
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div class="card">
|
|
34
|
+
<slot></slot>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```vue
|
|
40
|
+
<!-- Parent.vue -->
|
|
41
|
+
<template>
|
|
42
|
+
<!-- This NEVER fires! Native clicks are blocked -->
|
|
43
|
+
<ClickableCard @click="handleClick">
|
|
44
|
+
Click me
|
|
45
|
+
</ClickableCard>
|
|
46
|
+
</template>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Why it fails:**
|
|
50
|
+
1. `click` is declared in `emits`
|
|
51
|
+
2. Vue treats `@click` as a component event listener
|
|
52
|
+
3. Native click on the `<div>` doesn't trigger component event
|
|
53
|
+
4. Since `emit('click')` is never called, handler never fires
|
|
54
|
+
|
|
55
|
+
## The Solution
|
|
56
|
+
|
|
57
|
+
**Option 1: Emit the event explicitly:**
|
|
58
|
+
```vue
|
|
59
|
+
<!-- ClickableCard.vue -->
|
|
60
|
+
<script setup>
|
|
61
|
+
const emit = defineEmits(['click', 'select'])
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<template>
|
|
65
|
+
<!-- Explicitly emit click when div is clicked -->
|
|
66
|
+
<div class="card" @click="emit('click', $event)">
|
|
67
|
+
<slot></slot>
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Option 2: Don't declare native events (use fallthrough):**
|
|
73
|
+
```vue
|
|
74
|
+
<!-- ClickableCard.vue -->
|
|
75
|
+
<script setup>
|
|
76
|
+
// Only declare custom events, not native ones
|
|
77
|
+
const emit = defineEmits(['select', 'custom-action'])
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<template>
|
|
81
|
+
<!-- Native @click from parent falls through to this div -->
|
|
82
|
+
<div class="card">
|
|
83
|
+
<slot></slot>
|
|
84
|
+
</div>
|
|
85
|
+
</template>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```vue
|
|
89
|
+
<!-- Parent.vue -->
|
|
90
|
+
<template>
|
|
91
|
+
<!-- Native click falls through and works -->
|
|
92
|
+
<ClickableCard @click="handleClick">
|
|
93
|
+
Click me
|
|
94
|
+
</ClickableCard>
|
|
95
|
+
</template>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Native Events Affected
|
|
99
|
+
|
|
100
|
+
This applies to any native DOM event you might declare:
|
|
101
|
+
|
|
102
|
+
| Event | Behavior When Declared |
|
|
103
|
+
|-------|----------------------|
|
|
104
|
+
| `click` | Only responds to `emit('click')`, not native clicks |
|
|
105
|
+
| `input` | Only responds to `emit('input')`, not native input |
|
|
106
|
+
| `change` | Only responds to `emit('change')`, not native change |
|
|
107
|
+
| `focus` | Only responds to `emit('focus')`, not native focus |
|
|
108
|
+
| `blur` | Only responds to `emit('blur')`, not native blur |
|
|
109
|
+
| `submit` | Only responds to `emit('submit')`, not native form submit |
|
|
110
|
+
| `keydown` | Only responds to `emit('keydown')`, not native keydown |
|
|
111
|
+
|
|
112
|
+
## When This Is Intentional
|
|
113
|
+
|
|
114
|
+
Sometimes you WANT to intercept native events:
|
|
115
|
+
|
|
116
|
+
```vue
|
|
117
|
+
<!-- CustomInput.vue -->
|
|
118
|
+
<script setup>
|
|
119
|
+
// Intentionally intercept 'input' to transform the value
|
|
120
|
+
const emit = defineEmits(['input', 'update:modelValue'])
|
|
121
|
+
|
|
122
|
+
function handleInput(event) {
|
|
123
|
+
const transformedValue = event.target.value.toUpperCase()
|
|
124
|
+
emit('input', transformedValue) // Emit transformed value, not raw event
|
|
125
|
+
emit('update:modelValue', transformedValue)
|
|
126
|
+
}
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<template>
|
|
130
|
+
<input @input="handleInput" />
|
|
131
|
+
</template>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Here, declaring `input` is correct because you want to intercept and transform the native event before passing it to the parent.
|
|
135
|
+
|
|
136
|
+
## Debugging Tips
|
|
137
|
+
|
|
138
|
+
If your click handlers aren't firing:
|
|
139
|
+
|
|
140
|
+
1. Check if the event is declared in `emits`
|
|
141
|
+
2. If declared, ensure you're calling `emit('click')` somewhere
|
|
142
|
+
3. If you want native behavior, remove from `emits` declaration
|
|
143
|
+
4. Use Vue DevTools to see which events are being emitted
|
|
144
|
+
|
|
145
|
+
```vue
|
|
146
|
+
<script setup>
|
|
147
|
+
const emit = defineEmits(['click'])
|
|
148
|
+
|
|
149
|
+
function handleClick(event) {
|
|
150
|
+
console.log('Native click received, now emitting component event')
|
|
151
|
+
emit('click', event)
|
|
152
|
+
}
|
|
153
|
+
</script>
|
|
154
|
+
|
|
155
|
+
<template>
|
|
156
|
+
<div @click="handleClick">Click me</div>
|
|
157
|
+
</template>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Reference
|
|
161
|
+
- [Vue.js Component Events](https://vuejs.org/guide/components/events.html)
|
|
162
|
+
- [Vue.js Fallthrough Attributes](https://vuejs.org/guide/components/attrs.html)
|