@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,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: KeepAlive with Transition Memory Leak
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Combining KeepAlive with Transition can cause memory leaks in certain Vue versions
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, keepalive, transition, memory-leak, animation]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# KeepAlive with Transition Memory Leak
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - There is a known memory leak when using `<Transition>` and `<KeepAlive>` together. Component instances may not be properly freed from memory when combining these features.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Test memory behavior when using KeepAlive + Transition together
|
|
16
|
+
- [ ] Consider if transition animation is necessary with cached components
|
|
17
|
+
- [ ] Use browser DevTools Memory tab to verify no leak
|
|
18
|
+
- [ ] Keep Vue updated to get latest bug fixes
|
|
19
|
+
|
|
20
|
+
## The Problem
|
|
21
|
+
|
|
22
|
+
```vue
|
|
23
|
+
<template>
|
|
24
|
+
<!-- Known memory leak combination in some Vue versions -->
|
|
25
|
+
<Transition name="fade">
|
|
26
|
+
<KeepAlive>
|
|
27
|
+
<component :is="currentView" />
|
|
28
|
+
</KeepAlive>
|
|
29
|
+
</Transition>
|
|
30
|
+
</template>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
When switching between components repeatedly:
|
|
34
|
+
- Component instances accumulate in memory
|
|
35
|
+
- References prevent garbage collection
|
|
36
|
+
- Memory usage grows with each switch
|
|
37
|
+
|
|
38
|
+
## Diagnosis
|
|
39
|
+
|
|
40
|
+
Use Chrome DevTools to detect the leak:
|
|
41
|
+
|
|
42
|
+
1. Open DevTools > Memory tab
|
|
43
|
+
2. Take heap snapshot
|
|
44
|
+
3. Switch between components 10+ times
|
|
45
|
+
4. Take another heap snapshot
|
|
46
|
+
5. Compare: look for growing VueComponent count
|
|
47
|
+
|
|
48
|
+
## Workarounds
|
|
49
|
+
|
|
50
|
+
### Option 1: Remove Transition if Not Essential
|
|
51
|
+
|
|
52
|
+
```vue
|
|
53
|
+
<template>
|
|
54
|
+
<!-- No memory leak without Transition -->
|
|
55
|
+
<KeepAlive :max="5">
|
|
56
|
+
<component :is="currentView" />
|
|
57
|
+
</KeepAlive>
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Option 2: Use CSS Animations Instead
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<template>
|
|
65
|
+
<KeepAlive :max="5">
|
|
66
|
+
<component
|
|
67
|
+
:is="currentView"
|
|
68
|
+
:class="{ 'fade-enter': isTransitioning }"
|
|
69
|
+
/>
|
|
70
|
+
</KeepAlive>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<style>
|
|
74
|
+
.fade-enter {
|
|
75
|
+
animation: fadeIn 0.3s ease-in;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@keyframes fadeIn {
|
|
79
|
+
from { opacity: 0; }
|
|
80
|
+
to { opacity: 1; }
|
|
81
|
+
}
|
|
82
|
+
</style>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Option 3: Use Strict Cache Limits
|
|
86
|
+
|
|
87
|
+
If you must use both, minimize impact with strict limits:
|
|
88
|
+
|
|
89
|
+
```vue
|
|
90
|
+
<template>
|
|
91
|
+
<Transition name="fade" mode="out-in">
|
|
92
|
+
<KeepAlive :max="3">
|
|
93
|
+
<component :is="currentView" />
|
|
94
|
+
</KeepAlive>
|
|
95
|
+
</Transition>
|
|
96
|
+
</template>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Option 4: Key-Based Cache Invalidation
|
|
100
|
+
|
|
101
|
+
Force fresh instances when needed:
|
|
102
|
+
|
|
103
|
+
```vue
|
|
104
|
+
<script setup>
|
|
105
|
+
import { ref, computed } from 'vue'
|
|
106
|
+
|
|
107
|
+
const currentView = ref('Dashboard')
|
|
108
|
+
const cacheKey = ref(0)
|
|
109
|
+
|
|
110
|
+
function switchViewFresh(view) {
|
|
111
|
+
currentView.value = view
|
|
112
|
+
cacheKey.value++ // Force new instance
|
|
113
|
+
}
|
|
114
|
+
</script>
|
|
115
|
+
|
|
116
|
+
<template>
|
|
117
|
+
<Transition name="fade" mode="out-in">
|
|
118
|
+
<KeepAlive :max="3">
|
|
119
|
+
<component :is="currentView" :key="cacheKey" />
|
|
120
|
+
</KeepAlive>
|
|
121
|
+
</Transition>
|
|
122
|
+
</template>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Keep Vue Updated
|
|
126
|
+
|
|
127
|
+
This is a known issue tracked in Vue's GitHub repository. Memory leak fixes are periodically released, so ensure you're on the latest Vue version:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm update vue
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Key Points
|
|
134
|
+
|
|
135
|
+
1. **Known issue** - Memory leaks with KeepAlive + Transition are documented
|
|
136
|
+
2. **Test in DevTools** - Use Memory tab to verify your specific usage
|
|
137
|
+
3. **Consider alternatives** - CSS animations may work without the leak
|
|
138
|
+
4. **Set strict `max`** - Limit cache size to cap memory impact
|
|
139
|
+
5. **Keep Vue updated** - Bug fixes are released periodically
|
|
140
|
+
|
|
141
|
+
## Reference
|
|
142
|
+
- [GitHub Issue #9842: Memory leak with transition and keep-alive](https://github.com/vuejs/vue/issues/9842)
|
|
143
|
+
- [GitHub Issue #9840: Memory leak with transition and keep-alive](https://github.com/vuejs/vue/issues/9840)
|
|
144
|
+
- [Vue.js KeepAlive Documentation](https://vuejs.org/guide/built-ins/keep-alive.html)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: System Modifier Keys Must Be Held During keyup Events
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Modifier keys (ctrl, alt, shift, meta) behave differently with keyup - they must be held when the key is released
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, events, keyboard, modifiers, keyup, shortcuts]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# System Modifier Keys Must Be Held During keyup Events
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - When using system modifier keys (`.ctrl`, `.alt`, `.shift`, `.meta`) with `keyup` events, the modifier must still be pressed when the other key is released. Releasing the modifier key first will not trigger the event, causing keyboard shortcuts to appear broken.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Understand that `@keyup.ctrl` requires Ctrl to be held while releasing another key
|
|
16
|
+
- [ ] Consider using `keydown` instead of `keyup` for modifier key combinations
|
|
17
|
+
- [ ] Use `.exact` when you need precise modifier key control
|
|
18
|
+
- [ ] Test keyboard shortcuts with proper key release order
|
|
19
|
+
|
|
20
|
+
**Incorrect:**
|
|
21
|
+
```html
|
|
22
|
+
<!-- WRONG: Expecting this to fire when Ctrl is released -->
|
|
23
|
+
<template>
|
|
24
|
+
<input @keyup.ctrl="onCtrlRelease" />
|
|
25
|
+
<!-- This does NOT fire when you just release Ctrl! -->
|
|
26
|
+
<!-- It fires when you release ANY key while holding Ctrl -->
|
|
27
|
+
</template>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<!-- WRONG: Misunderstanding keyup.ctrl behavior -->
|
|
32
|
+
<template>
|
|
33
|
+
<div @keyup.ctrl="handleShortcut">
|
|
34
|
+
<!-- User presses Ctrl+S, releases Ctrl first, then S -->
|
|
35
|
+
<!-- Event does NOT fire because Ctrl wasn't held during S release -->
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Correct:**
|
|
41
|
+
```html
|
|
42
|
+
<!-- CORRECT: User must hold Ctrl while releasing another key -->
|
|
43
|
+
<template>
|
|
44
|
+
<input @keyup.ctrl.s="saveDocument" />
|
|
45
|
+
<!-- User presses Ctrl+S, then releases S while holding Ctrl -->
|
|
46
|
+
<!-- Event fires correctly -->
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<script setup>
|
|
50
|
+
function saveDocument(event) {
|
|
51
|
+
event.preventDefault()
|
|
52
|
+
// Save logic here
|
|
53
|
+
}
|
|
54
|
+
</script>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<!-- CORRECT: Use keydown for more intuitive modifier behavior -->
|
|
59
|
+
<template>
|
|
60
|
+
<div @keydown.ctrl.s="saveDocument">
|
|
61
|
+
<!-- keydown fires immediately when both keys are pressed -->
|
|
62
|
+
<!-- More intuitive for keyboard shortcuts -->
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```html
|
|
68
|
+
<!-- CORRECT: Use .exact for precise modifier control -->
|
|
69
|
+
<template>
|
|
70
|
+
<!-- Only fires when ONLY Ctrl is pressed (no Shift, Alt, etc.) -->
|
|
71
|
+
<button @click.ctrl.exact="onCtrlClick">Ctrl+Click Only</button>
|
|
72
|
+
|
|
73
|
+
<!-- Fires with no system modifiers at all -->
|
|
74
|
+
<button @click.exact="onPlainClick">Plain Click Only</button>
|
|
75
|
+
</template>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## How System Modifiers Work with keyup
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
// Timeline of Ctrl+S keydown:
|
|
82
|
+
// 1. User presses Ctrl (keydown fires)
|
|
83
|
+
// 2. User presses S while holding Ctrl (keydown fires)
|
|
84
|
+
|
|
85
|
+
// Timeline of Ctrl+S keyup:
|
|
86
|
+
// 3. User releases S while holding Ctrl (keyup.ctrl.s fires!)
|
|
87
|
+
// 4. User releases Ctrl (keyup fires, but not keyup.ctrl.s)
|
|
88
|
+
|
|
89
|
+
// Common mistake:
|
|
90
|
+
// 3. User releases Ctrl first (nothing fires for our handler)
|
|
91
|
+
// 4. User releases S (keyup.s fires, but not keyup.ctrl.s)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## System Modifier Keys
|
|
95
|
+
|
|
96
|
+
```html
|
|
97
|
+
<!-- Available system modifiers -->
|
|
98
|
+
<input @keyup.ctrl="..." /> <!-- Ctrl key -->
|
|
99
|
+
<input @keyup.alt="..." /> <!-- Alt key (Option on Mac) -->
|
|
100
|
+
<input @keyup.shift="..." /> <!-- Shift key -->
|
|
101
|
+
<input @keyup.meta="..." /> <!-- Cmd on Mac, Windows key on PC -->
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## The .exact Modifier
|
|
105
|
+
|
|
106
|
+
```html
|
|
107
|
+
<!-- Different .exact behaviors -->
|
|
108
|
+
|
|
109
|
+
<!-- Fires even if Shift/Alt are also pressed -->
|
|
110
|
+
<button @click.ctrl="onClick">Ctrl + any other modifiers</button>
|
|
111
|
+
|
|
112
|
+
<!-- Fires ONLY when Ctrl alone is pressed -->
|
|
113
|
+
<button @click.ctrl.exact="onClick">Ctrl only, no other modifiers</button>
|
|
114
|
+
|
|
115
|
+
<!-- Fires ONLY when no system modifiers are pressed -->
|
|
116
|
+
<button @click.exact="onClick">No modifiers allowed</button>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Best Practice: Prefer keydown for Shortcuts
|
|
120
|
+
|
|
121
|
+
```html
|
|
122
|
+
<template>
|
|
123
|
+
<div
|
|
124
|
+
tabindex="0"
|
|
125
|
+
@keydown.ctrl.s.prevent="save"
|
|
126
|
+
@keydown.ctrl.z.prevent="undo"
|
|
127
|
+
@keydown.ctrl.shift.z.prevent="redo"
|
|
128
|
+
>
|
|
129
|
+
<!-- keydown is more reliable for keyboard shortcuts -->
|
|
130
|
+
<!-- Add .prevent to stop browser default (e.g., save dialog) -->
|
|
131
|
+
</div>
|
|
132
|
+
</template>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Reference
|
|
136
|
+
- [Vue.js Event Handling - Key Modifiers](https://vuejs.org/guide/essentials/event-handling.html#key-modifiers)
|
|
137
|
+
- [Vue.js Event Handling - System Modifier Keys](https://vuejs.org/guide/essentials/event-handling.html#system-modifier-keys)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Access DOM Only After Mounted Hook
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Accessing DOM elements before mounted causes undefined errors and silent failures
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, vue2, lifecycle, dom, mounted, created, beforeMount, template-refs]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Access DOM Only After Mounted Hook
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - Attempting to access DOM elements or `this.$el` in `created` or `beforeMount` hooks fails because the component's template has not yet been rendered to the DOM. This leads to undefined errors, null references, and failed third-party library initializations.
|
|
12
|
+
|
|
13
|
+
The component's DOM is only available starting from the `mounted` hook (Options API) or after `onMounted` runs (Composition API). Before this point, `this.$el` is undefined and template refs are null.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Perform DOM manipulations only in `mounted`/`onMounted` or later
|
|
18
|
+
- [ ] Initialize DOM-dependent libraries (charts, maps, editors) in mounted
|
|
19
|
+
- [ ] Use `created` for data initialization and API calls (non-DOM operations)
|
|
20
|
+
- [ ] Access template refs only after mounted
|
|
21
|
+
- [ ] Use `$nextTick` if you need DOM after reactive data changes
|
|
22
|
+
|
|
23
|
+
**Incorrect:**
|
|
24
|
+
```javascript
|
|
25
|
+
// WRONG: Accessing DOM in created hook
|
|
26
|
+
export default {
|
|
27
|
+
created() {
|
|
28
|
+
// DOM doesn't exist yet!
|
|
29
|
+
console.log(this.$el) // undefined
|
|
30
|
+
this.$el.querySelector('.chart') // Error: Cannot read property 'querySelector' of undefined
|
|
31
|
+
|
|
32
|
+
// Third-party library initialization fails
|
|
33
|
+
new Chart(document.getElementById('myChart')) // Element doesn't exist yet
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
// WRONG: Accessing DOM in beforeMount
|
|
40
|
+
export default {
|
|
41
|
+
beforeMount() {
|
|
42
|
+
// Still too early - template is compiled but not mounted
|
|
43
|
+
console.log(this.$el) // undefined in Vue 3
|
|
44
|
+
this.$refs.myInput.focus() // Error: Cannot read property 'focus' of undefined
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<!-- WRONG: Accessing template ref synchronously in setup -->
|
|
51
|
+
<script setup>
|
|
52
|
+
import { ref } from 'vue'
|
|
53
|
+
|
|
54
|
+
const myInput = ref(null)
|
|
55
|
+
|
|
56
|
+
// This runs during setup, before mounting
|
|
57
|
+
myInput.value.focus() // Error: Cannot read property 'focus' of null
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<template>
|
|
61
|
+
<input ref="myInput" />
|
|
62
|
+
</template>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Correct:**
|
|
66
|
+
```javascript
|
|
67
|
+
// CORRECT: Use created for data, mounted for DOM
|
|
68
|
+
export default {
|
|
69
|
+
data() {
|
|
70
|
+
return { chartData: null }
|
|
71
|
+
},
|
|
72
|
+
async created() {
|
|
73
|
+
// Data fetching is fine in created
|
|
74
|
+
this.chartData = await fetchChartData()
|
|
75
|
+
},
|
|
76
|
+
mounted() {
|
|
77
|
+
// Now the DOM exists and is safe to access
|
|
78
|
+
console.log(this.$el) // <div>...</div>
|
|
79
|
+
|
|
80
|
+
// Initialize DOM-dependent libraries
|
|
81
|
+
this.chart = new Chart(this.$refs.chartCanvas, {
|
|
82
|
+
data: this.chartData
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```vue
|
|
89
|
+
<!-- CORRECT: Access template refs in onMounted -->
|
|
90
|
+
<script setup>
|
|
91
|
+
import { ref, onMounted } from 'vue'
|
|
92
|
+
|
|
93
|
+
const myInput = ref(null)
|
|
94
|
+
|
|
95
|
+
onMounted(() => {
|
|
96
|
+
// DOM is now available
|
|
97
|
+
myInput.value.focus() // Works!
|
|
98
|
+
})
|
|
99
|
+
</script>
|
|
100
|
+
|
|
101
|
+
<template>
|
|
102
|
+
<input ref="myInput" />
|
|
103
|
+
</template>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
// CORRECT: Using $nextTick for DOM access after data changes
|
|
108
|
+
export default {
|
|
109
|
+
methods: {
|
|
110
|
+
async addItem() {
|
|
111
|
+
this.items.push(newItem)
|
|
112
|
+
|
|
113
|
+
// Wait for Vue to update the DOM
|
|
114
|
+
await this.$nextTick()
|
|
115
|
+
|
|
116
|
+
// Now the new element exists in DOM
|
|
117
|
+
this.$refs.list.lastElementChild.scrollIntoView()
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Vue 3 Composition API Pattern
|
|
124
|
+
|
|
125
|
+
```vue
|
|
126
|
+
<script setup>
|
|
127
|
+
import { ref, onMounted, nextTick } from 'vue'
|
|
128
|
+
|
|
129
|
+
const listRef = ref(null)
|
|
130
|
+
const items = ref([])
|
|
131
|
+
|
|
132
|
+
onMounted(() => {
|
|
133
|
+
// Safe to access DOM here
|
|
134
|
+
listRef.value.style.height = '400px'
|
|
135
|
+
})
|
|
136
|
+
</script>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Vue 3.5+ useTemplateRef Pattern
|
|
140
|
+
|
|
141
|
+
```vue
|
|
142
|
+
<script setup>
|
|
143
|
+
import { useTemplateRef, onMounted } from 'vue'
|
|
144
|
+
|
|
145
|
+
// Vue 3.5+ recommended approach - decouples ref name from variable name
|
|
146
|
+
const input = useTemplateRef('my-input')
|
|
147
|
+
|
|
148
|
+
onMounted(() => {
|
|
149
|
+
input.value.focus()
|
|
150
|
+
})
|
|
151
|
+
</script>
|
|
152
|
+
|
|
153
|
+
<template>
|
|
154
|
+
<input ref="my-input" />
|
|
155
|
+
</template>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Composition API with nextTick
|
|
159
|
+
|
|
160
|
+
```vue
|
|
161
|
+
<script setup>
|
|
162
|
+
import { ref, nextTick } from 'vue'
|
|
163
|
+
|
|
164
|
+
const listRef = ref(null)
|
|
165
|
+
const items = ref([])
|
|
166
|
+
|
|
167
|
+
async function addItem(item) {
|
|
168
|
+
items.value.push(item)
|
|
169
|
+
|
|
170
|
+
// Wait for DOM update after reactive change
|
|
171
|
+
await nextTick()
|
|
172
|
+
|
|
173
|
+
// Now new item is in DOM
|
|
174
|
+
listRef.value.lastElementChild.focus()
|
|
175
|
+
}
|
|
176
|
+
</script>
|
|
177
|
+
|
|
178
|
+
<template>
|
|
179
|
+
<ul ref="listRef">
|
|
180
|
+
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
|
|
181
|
+
</ul>
|
|
182
|
+
</template>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Common Third-Party Libraries
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
// CORRECT: Initialize in mounted
|
|
189
|
+
export default {
|
|
190
|
+
mounted() {
|
|
191
|
+
// Chart.js
|
|
192
|
+
this.chart = new Chart(this.$refs.canvas, config)
|
|
193
|
+
|
|
194
|
+
// Leaflet maps
|
|
195
|
+
this.map = L.map(this.$refs.mapContainer).setView([51.505, -0.09], 13)
|
|
196
|
+
|
|
197
|
+
// Monaco Editor
|
|
198
|
+
this.editor = monaco.editor.create(this.$refs.editorContainer, options)
|
|
199
|
+
|
|
200
|
+
// Video.js
|
|
201
|
+
this.player = videojs(this.$refs.videoElement)
|
|
202
|
+
},
|
|
203
|
+
beforeUnmount() {
|
|
204
|
+
// Don't forget cleanup!
|
|
205
|
+
this.chart?.destroy()
|
|
206
|
+
this.map?.remove()
|
|
207
|
+
this.editor?.dispose()
|
|
208
|
+
this.player?.dispose()
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Reference
|
|
214
|
+
- [Vue.js Lifecycle Hooks](https://vuejs.org/guide/essentials/lifecycle.html)
|
|
215
|
+
- [Vue.js Template Refs](https://vuejs.org/guide/essentials/template-refs.html)
|
|
216
|
+
- [Vue.js nextTick](https://vuejs.org/api/general.html#nexttick)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Register Lifecycle Hooks Synchronously During Setup
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Asynchronously registered lifecycle hooks will never execute
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, composition-api, lifecycle, onMounted, onUnmounted, async, setup]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Register Lifecycle Hooks Synchronously During Setup
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - Lifecycle hooks registered asynchronously (e.g., inside setTimeout, after await) will never be called because Vue cannot associate them with the component instance. This leads to silent failures where expected initialization or cleanup code never runs.
|
|
12
|
+
|
|
13
|
+
In Vue 3's Composition API, lifecycle hooks like `onMounted`, `onUnmounted`, `onUpdated`, etc. must be registered synchronously during component setup. The hook registration doesn't need to be lexically inside `setup()` or `<script setup>`, but the call stack must be synchronous and originate from within setup.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Register all lifecycle hooks at the top level of setup() or `<script setup>`
|
|
18
|
+
- [ ] Never register hooks inside setTimeout, setInterval, or Promise callbacks
|
|
19
|
+
- [ ] When calling composables that use lifecycle hooks, call them synchronously
|
|
20
|
+
- [ ] Hooks CAN be in external functions if called synchronously from setup
|
|
21
|
+
|
|
22
|
+
**Incorrect:**
|
|
23
|
+
```javascript
|
|
24
|
+
// WRONG: Hook registered asynchronously - will NEVER execute
|
|
25
|
+
import { onMounted } from 'vue'
|
|
26
|
+
|
|
27
|
+
export default {
|
|
28
|
+
async setup() {
|
|
29
|
+
// After await, we're in a different call stack
|
|
30
|
+
const data = await fetchInitialData()
|
|
31
|
+
|
|
32
|
+
// This hook will NOT be registered!
|
|
33
|
+
onMounted(() => {
|
|
34
|
+
console.log('This will never run')
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
// WRONG: Hook registered in setTimeout - will NEVER execute
|
|
42
|
+
import { onMounted } from 'vue'
|
|
43
|
+
|
|
44
|
+
export default {
|
|
45
|
+
setup() {
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
// This is asynchronous - hook won't be registered!
|
|
48
|
+
onMounted(() => {
|
|
49
|
+
initializeChart()
|
|
50
|
+
})
|
|
51
|
+
}, 100)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
// WRONG: Hook registered in Promise callback
|
|
58
|
+
import { onMounted } from 'vue'
|
|
59
|
+
|
|
60
|
+
export default {
|
|
61
|
+
setup() {
|
|
62
|
+
fetchConfig().then(() => {
|
|
63
|
+
// Asynchronous! This will silently fail
|
|
64
|
+
onMounted(() => {
|
|
65
|
+
applyConfig()
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Correct:**
|
|
73
|
+
```javascript
|
|
74
|
+
// CORRECT: Hook registered synchronously at top level
|
|
75
|
+
import { onMounted, ref } from 'vue'
|
|
76
|
+
|
|
77
|
+
export default {
|
|
78
|
+
setup() {
|
|
79
|
+
const data = ref(null)
|
|
80
|
+
|
|
81
|
+
// Register hook synchronously FIRST
|
|
82
|
+
onMounted(async () => {
|
|
83
|
+
// Async operations are fine INSIDE the hook
|
|
84
|
+
data.value = await fetchInitialData()
|
|
85
|
+
initializeChart()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
return { data }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```vue
|
|
94
|
+
<!-- CORRECT: <script setup> - hooks at top level -->
|
|
95
|
+
<script setup>
|
|
96
|
+
import { onMounted, onUnmounted, ref } from 'vue'
|
|
97
|
+
|
|
98
|
+
const isReady = ref(false)
|
|
99
|
+
|
|
100
|
+
// These are synchronous during script setup execution
|
|
101
|
+
onMounted(() => {
|
|
102
|
+
isReady.value = true
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
onUnmounted(() => {
|
|
106
|
+
cleanup()
|
|
107
|
+
})
|
|
108
|
+
</script>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
// CORRECT: Hook in external function called synchronously from setup
|
|
113
|
+
import { onMounted, onUnmounted } from 'vue'
|
|
114
|
+
|
|
115
|
+
function useWindowResize(callback) {
|
|
116
|
+
// This is fine - it's called synchronously from setup
|
|
117
|
+
onMounted(() => {
|
|
118
|
+
window.addEventListener('resize', callback)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
onUnmounted(() => {
|
|
122
|
+
window.removeEventListener('resize', callback)
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export default {
|
|
127
|
+
setup() {
|
|
128
|
+
// Composable called synchronously - hooks will be registered
|
|
129
|
+
useWindowResize(handleResize)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Multiple Hooks Are Allowed
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
// CORRECT: You can register the same hook multiple times
|
|
138
|
+
import { onMounted } from 'vue'
|
|
139
|
+
|
|
140
|
+
export default {
|
|
141
|
+
setup() {
|
|
142
|
+
// Both will run, in order of registration
|
|
143
|
+
onMounted(() => {
|
|
144
|
+
initializeA()
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
onMounted(() => {
|
|
148
|
+
initializeB()
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Reference
|
|
155
|
+
- [Vue.js Lifecycle Hooks](https://vuejs.org/guide/essentials/lifecycle.html)
|
|
156
|
+
- [Composition API Lifecycle Hooks](https://vuejs.org/api/composition-api-lifecycle.html)
|