@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,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Never Use .passive and .prevent Together
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Conflicting modifiers cause .prevent to be ignored and trigger browser warnings
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, events, modifiers, scroll, touch, performance]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Never Use .passive and .prevent Together
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - The `.passive` modifier tells the browser you will NOT call `preventDefault()`, while `.prevent` does exactly that. Using them together causes `.prevent` to be ignored and triggers browser console warnings. This is a logical contradiction that leads to broken event handling.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Never combine `.passive` and `.prevent` on the same event
|
|
16
|
+
- [ ] Use `.passive` for scroll/touch events where you want better performance
|
|
17
|
+
- [ ] Use `.prevent` when you need to stop the default browser action
|
|
18
|
+
- [ ] If you need conditional prevention, handle it in JavaScript without `.passive`
|
|
19
|
+
|
|
20
|
+
**Incorrect:**
|
|
21
|
+
```html
|
|
22
|
+
<!-- WRONG: Conflicting modifiers -->
|
|
23
|
+
<template>
|
|
24
|
+
<div @scroll.passive.prevent="handleScroll">
|
|
25
|
+
<!-- .prevent will be IGNORED -->
|
|
26
|
+
<!-- Browser shows warning -->
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<!-- WRONG: On touch events -->
|
|
33
|
+
<template>
|
|
34
|
+
<div @touchstart.passive.prevent="handleTouch">
|
|
35
|
+
<!-- Cannot prevent default - passive already promised not to -->
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<!-- WRONG: On wheel events -->
|
|
42
|
+
<template>
|
|
43
|
+
<div @wheel.passive.prevent="handleWheel">
|
|
44
|
+
<!-- Broken: will scroll anyway despite .prevent -->
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Correct:**
|
|
50
|
+
```html
|
|
51
|
+
<!-- CORRECT: Use .passive for performance (no prevention needed) -->
|
|
52
|
+
<template>
|
|
53
|
+
<div @scroll.passive="handleScroll">
|
|
54
|
+
<!-- Good for scroll tracking without blocking -->
|
|
55
|
+
</div>
|
|
56
|
+
</template>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<!-- CORRECT: Use .prevent when you need to prevent default -->
|
|
61
|
+
<template>
|
|
62
|
+
<form @submit.prevent="handleSubmit">
|
|
63
|
+
<!-- Correctly prevents form submission -->
|
|
64
|
+
</form>
|
|
65
|
+
</template>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```html
|
|
69
|
+
<!-- CORRECT: For touch events where you need to prevent -->
|
|
70
|
+
<template>
|
|
71
|
+
<div @touchmove="handleTouchMove">
|
|
72
|
+
<!-- Handle prevention conditionally in JS -->
|
|
73
|
+
</div>
|
|
74
|
+
</template>
|
|
75
|
+
|
|
76
|
+
<script setup>
|
|
77
|
+
function handleTouchMove(event) {
|
|
78
|
+
if (shouldPreventScroll.value) {
|
|
79
|
+
event.preventDefault()
|
|
80
|
+
}
|
|
81
|
+
// ... handle touch
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Understanding .passive
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
// .passive tells the browser:
|
|
90
|
+
// "I promise I won't call preventDefault()"
|
|
91
|
+
|
|
92
|
+
// This allows the browser to:
|
|
93
|
+
// 1. Start scrolling immediately without waiting for JS
|
|
94
|
+
// 2. Improve scroll performance, especially on mobile
|
|
95
|
+
// 3. Reduce jank and stuttering
|
|
96
|
+
|
|
97
|
+
// Equivalent to:
|
|
98
|
+
element.addEventListener('scroll', handler, { passive: true })
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## When to Use .passive
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<!-- Good use cases for .passive -->
|
|
105
|
+
|
|
106
|
+
<!-- Scroll tracking analytics -->
|
|
107
|
+
<div @scroll.passive="trackScrollPosition">
|
|
108
|
+
|
|
109
|
+
<!-- Touch gesture detection (no prevention needed) -->
|
|
110
|
+
<div @touchmove.passive="detectGesture">
|
|
111
|
+
|
|
112
|
+
<!-- Wheel event monitoring -->
|
|
113
|
+
<div @wheel.passive="monitorWheel">
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## When to Use .prevent (Without .passive)
|
|
117
|
+
|
|
118
|
+
```html
|
|
119
|
+
<!-- Good use cases for .prevent -->
|
|
120
|
+
|
|
121
|
+
<!-- Form submission -->
|
|
122
|
+
<form @submit.prevent="handleSubmit">
|
|
123
|
+
|
|
124
|
+
<!-- Link clicks with custom navigation -->
|
|
125
|
+
<a @click.prevent="navigate">
|
|
126
|
+
|
|
127
|
+
<!-- Preventing context menu -->
|
|
128
|
+
<div @contextmenu.prevent="showCustomMenu">
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Browser Warning
|
|
132
|
+
|
|
133
|
+
When you combine `.passive` and `.prevent`, the browser console shows:
|
|
134
|
+
```
|
|
135
|
+
[Intervention] Unable to preventDefault inside passive event listener
|
|
136
|
+
due to target being treated as passive.
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Reference
|
|
140
|
+
- [Vue.js Event Handling - Event Modifiers](https://vuejs.org/guide/essentials/event-handling.html#event-modifiers)
|
|
141
|
+
- [MDN - Improving scroll performance with passive listeners](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Never Use v-if and v-for on the Same Element
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Causes confusing precedence issues and Vue 2 to 3 migration bugs
|
|
5
|
+
type: capability
|
|
6
|
+
tags: [vue3, v-if, v-for, conditional-rendering, list-rendering, eslint]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Never Use v-if and v-for on the Same Element
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - Using `v-if` and `v-for` on the same element creates ambiguous precedence that differs between Vue 2 and Vue 3. In Vue 2, `v-for` had higher precedence; in Vue 3, `v-if` has higher precedence. This breaking change causes subtle bugs during migration and makes code intent unclear.
|
|
12
|
+
|
|
13
|
+
The ESLint rule `vue/no-use-v-if-with-v-for` enforces this best practice.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Never place v-if and v-for on the same element
|
|
18
|
+
- [ ] For filtering list items: use a computed property that filters the array
|
|
19
|
+
- [ ] For hiding entire list: wrap with `<template v-if>` around the v-for
|
|
20
|
+
- [ ] Enable eslint-plugin-vue rule `vue/no-use-v-if-with-v-for`
|
|
21
|
+
|
|
22
|
+
**Incorrect:**
|
|
23
|
+
```html
|
|
24
|
+
<!-- WRONG: v-if and v-for on same element - ambiguous precedence -->
|
|
25
|
+
<template>
|
|
26
|
+
<!-- Intent: show only active users -->
|
|
27
|
+
<li v-for="user in users" v-if="user.isActive" :key="user.id">
|
|
28
|
+
{{ user.name }}
|
|
29
|
+
</li>
|
|
30
|
+
</template>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<!-- WRONG: Hiding entire list conditionally -->
|
|
35
|
+
<template>
|
|
36
|
+
<li v-for="user in users" v-if="shouldShowList" :key="user.id">
|
|
37
|
+
{{ user.name }}
|
|
38
|
+
</li>
|
|
39
|
+
</template>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<!-- WRONG: Vue 3 precedence issue -->
|
|
44
|
+
<template>
|
|
45
|
+
<!-- In Vue 3, v-if runs FIRST, so 'user' is undefined! -->
|
|
46
|
+
<li v-for="user in users" v-if="user.isActive" :key="user.id">
|
|
47
|
+
{{ user.name }}
|
|
48
|
+
</li>
|
|
49
|
+
<!-- Error: Cannot read property 'isActive' of undefined -->
|
|
50
|
+
</template>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Correct:**
|
|
54
|
+
```html
|
|
55
|
+
<!-- CORRECT: Filter with computed property -->
|
|
56
|
+
<template>
|
|
57
|
+
<li v-for="user in activeUsers" :key="user.id">
|
|
58
|
+
{{ user.name }}
|
|
59
|
+
</li>
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<script setup>
|
|
63
|
+
import { computed } from 'vue'
|
|
64
|
+
|
|
65
|
+
const props = defineProps(['users'])
|
|
66
|
+
|
|
67
|
+
const activeUsers = computed(() =>
|
|
68
|
+
props.users.filter(user => user.isActive)
|
|
69
|
+
)
|
|
70
|
+
</script>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<!-- CORRECT: Wrap with <template v-if> for conditional list -->
|
|
75
|
+
<template>
|
|
76
|
+
<template v-if="shouldShowList">
|
|
77
|
+
<li v-for="user in users" :key="user.id">
|
|
78
|
+
{{ user.name }}
|
|
79
|
+
</li>
|
|
80
|
+
</template>
|
|
81
|
+
</template>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```html
|
|
85
|
+
<!-- CORRECT: v-if inside the loop (per-item condition) -->
|
|
86
|
+
<template>
|
|
87
|
+
<ul>
|
|
88
|
+
<template v-for="user in users" :key="user.id">
|
|
89
|
+
<li v-if="user.isActive">
|
|
90
|
+
{{ user.name }}
|
|
91
|
+
</li>
|
|
92
|
+
</template>
|
|
93
|
+
</ul>
|
|
94
|
+
</template>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Vue 2 vs Vue 3 Precedence Change
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
// Vue 2: v-for evaluated first
|
|
101
|
+
// <li v-for="user in users" v-if="user.isActive">
|
|
102
|
+
// Equivalent to: users.forEach(user => { if (user.isActive) render(user) })
|
|
103
|
+
|
|
104
|
+
// Vue 3: v-if evaluated first
|
|
105
|
+
// <li v-for="user in users" v-if="user.isActive">
|
|
106
|
+
// Equivalent to: if (user.isActive) users.forEach(user => render(user))
|
|
107
|
+
// Problem: 'user' doesn't exist yet when v-if runs!
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Why Computed Properties Are Better
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
// Benefits of filtering via computed:
|
|
114
|
+
// 1. Clear separation of concerns (logic vs template)
|
|
115
|
+
// 2. Cached - only recalculates when dependencies change
|
|
116
|
+
// 3. Reusable - can be used elsewhere in component
|
|
117
|
+
// 4. Testable - can unit test the filtering logic
|
|
118
|
+
// 5. No ambiguity about intent
|
|
119
|
+
|
|
120
|
+
const activeUsers = computed(() =>
|
|
121
|
+
users.value.filter(u => u.isActive)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
// Can add more complex filtering
|
|
125
|
+
const filteredUsers = computed(() =>
|
|
126
|
+
users.value
|
|
127
|
+
.filter(u => u.isActive)
|
|
128
|
+
.filter(u => u.role === selectedRole.value)
|
|
129
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
130
|
+
)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Reference
|
|
134
|
+
- [Vue.js Style Guide - Avoid v-if with v-for](https://vuejs.org/style-guide/rules-essential.html#avoid-v-if-with-v-for)
|
|
135
|
+
- [Vue 3 Migration Guide - v-if vs v-for Precedence](https://v3-migration.vuejs.org/breaking-changes/v-if-v-for)
|
|
136
|
+
- [ESLint Plugin Vue - no-use-v-if-with-v-for](https://eslint.vuejs.org/rules/no-use-v-if-with-v-for)
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Return Stable Object References from Computed Properties
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Computed properties returning new objects trigger effects even when values haven't meaningfully changed
|
|
5
|
+
type: efficiency
|
|
6
|
+
tags: [vue3, computed, performance, reactivity, vue3.4]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Return Stable Object References from Computed Properties
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - In Vue 3.4+, computed properties only trigger effects when their value changes. However, if a computed returns a new object each time, Vue cannot detect that the values inside are the same. This causes unnecessary effect re-runs.
|
|
12
|
+
|
|
13
|
+
For primitive values, Vue 3.4+ handles this automatically. For objects, manually compare and return the previous value when nothing meaningful has changed.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] For computed properties returning primitives, Vue 3.4+ handles stability automatically
|
|
18
|
+
- [ ] For computed properties returning objects, compare with previous value and return old reference if unchanged
|
|
19
|
+
- [ ] Always perform the full computation before comparing (to track dependencies correctly)
|
|
20
|
+
- [ ] Consider if you really need to return an object, or if primitives would suffice
|
|
21
|
+
|
|
22
|
+
**Incorrect:**
|
|
23
|
+
```vue
|
|
24
|
+
<script setup>
|
|
25
|
+
import { ref, computed, watchEffect } from 'vue'
|
|
26
|
+
|
|
27
|
+
const count = ref(0)
|
|
28
|
+
|
|
29
|
+
// BAD: Returns new object every time, always triggers effects
|
|
30
|
+
const stats = computed(() => {
|
|
31
|
+
return {
|
|
32
|
+
isEven: count.value % 2 === 0,
|
|
33
|
+
doubleValue: count.value * 2
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
watchEffect(() => {
|
|
38
|
+
console.log('Stats changed:', stats.value)
|
|
39
|
+
// Logs on EVERY count change, even when isEven hasn't changed
|
|
40
|
+
// count: 0 -> 2 -> 4: isEven is always true, but effect runs each time
|
|
41
|
+
})
|
|
42
|
+
</script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Correct:**
|
|
46
|
+
```vue
|
|
47
|
+
<script setup>
|
|
48
|
+
import { ref, computed, watchEffect } from 'vue'
|
|
49
|
+
|
|
50
|
+
const count = ref(0)
|
|
51
|
+
|
|
52
|
+
// GOOD (Vue 3.4+): Primitive computed - automatic stability
|
|
53
|
+
const isEven = computed(() => count.value % 2 === 0)
|
|
54
|
+
|
|
55
|
+
watchEffect(() => {
|
|
56
|
+
console.log('isEven:', isEven.value)
|
|
57
|
+
// Only logs when isEven actually changes (0, 2, 4 won't re-trigger)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// GOOD (Vue 3.4+): Manual comparison for object returns
|
|
61
|
+
const stats = computed((oldValue) => {
|
|
62
|
+
// Step 1: Always compute the new value first (to track dependencies)
|
|
63
|
+
const newValue = {
|
|
64
|
+
isEven: count.value % 2 === 0,
|
|
65
|
+
category: count.value < 10 ? 'small' : 'large'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Step 2: Compare with previous value
|
|
69
|
+
if (oldValue &&
|
|
70
|
+
oldValue.isEven === newValue.isEven &&
|
|
71
|
+
oldValue.category === newValue.category) {
|
|
72
|
+
return oldValue // Return old reference - no effect triggers
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return newValue
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
watchEffect(() => {
|
|
79
|
+
console.log('Stats changed:', stats.value)
|
|
80
|
+
// Now only logs when isEven or category actually changes
|
|
81
|
+
})
|
|
82
|
+
</script>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Primitive vs Object Computed Behavior (Vue 3.4+)
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
import { ref, computed, watchEffect } from 'vue'
|
|
89
|
+
|
|
90
|
+
const count = ref(0)
|
|
91
|
+
|
|
92
|
+
// PRIMITIVE: Vue automatically detects value hasn't changed
|
|
93
|
+
const isEven = computed(() => count.value % 2 === 0)
|
|
94
|
+
|
|
95
|
+
watchEffect(() => console.log(isEven.value)) // true
|
|
96
|
+
|
|
97
|
+
count.value = 2 // isEven still true - NO log
|
|
98
|
+
count.value = 4 // isEven still true - NO log
|
|
99
|
+
count.value = 3 // isEven now false - logs: false
|
|
100
|
+
|
|
101
|
+
// OBJECT: New reference every time (without manual comparison)
|
|
102
|
+
const obj = computed(() => ({ isEven: count.value % 2 === 0 }))
|
|
103
|
+
|
|
104
|
+
watchEffect(() => console.log(obj.value)) // { isEven: true }
|
|
105
|
+
|
|
106
|
+
count.value = 2 // Logs again! New object reference
|
|
107
|
+
count.value = 4 // Logs again! New object reference
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Advanced: Deep Object Comparison
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
import { ref, computed } from 'vue'
|
|
114
|
+
import { isEqual } from 'lodash-es' // For deep comparison
|
|
115
|
+
|
|
116
|
+
const filters = ref({ category: 'all', sortBy: 'date', page: 1 })
|
|
117
|
+
|
|
118
|
+
// For complex objects, use deep comparison
|
|
119
|
+
const activeFilters = computed((oldValue) => {
|
|
120
|
+
const newValue = {
|
|
121
|
+
...filters.value,
|
|
122
|
+
hasFilters: filters.value.category !== 'all' || filters.value.sortBy !== 'date'
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Deep compare for complex objects
|
|
126
|
+
if (oldValue && isEqual(oldValue, newValue)) {
|
|
127
|
+
return oldValue
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return newValue
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Important: Always Compute Before Comparing
|
|
135
|
+
|
|
136
|
+
```javascript
|
|
137
|
+
// BAD: Early return prevents dependency tracking
|
|
138
|
+
const optimized = computed((oldValue) => {
|
|
139
|
+
if (oldValue && someCondition) {
|
|
140
|
+
return oldValue // Dependencies not tracked!
|
|
141
|
+
}
|
|
142
|
+
return computeExpensiveValue()
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// GOOD: Compute first, then compare
|
|
146
|
+
const optimized = computed((oldValue) => {
|
|
147
|
+
const newValue = computeExpensiveValue() // Always track dependencies
|
|
148
|
+
if (oldValue && newValue === oldValue) {
|
|
149
|
+
return oldValue
|
|
150
|
+
}
|
|
151
|
+
return newValue
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Reference
|
|
156
|
+
- [Vue.js Performance - Computed Stability](https://vuejs.org/guide/best-practices/performance.html#computed-stability)
|
|
157
|
+
- [Vue.js Computed Properties](https://vuejs.org/guide/essentials/computed.html)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Keep Props Stable to Minimize Child Re-renders
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Passing changing props to list items causes ALL children to re-render unnecessarily
|
|
5
|
+
type: efficiency
|
|
6
|
+
tags: [vue3, performance, props, v-for, re-renders, optimization]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Keep Props Stable to Minimize Child Re-renders
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - When props passed to child components change, Vue must re-render those components. Passing derived values like `activeId` to every list item causes all items to re-render when activeId changes, even if only one item's active state actually changed.
|
|
12
|
+
|
|
13
|
+
Move comparison logic to the parent and pass the boolean result instead. This is one of the most impactful update performance optimizations in Vue.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Avoid passing parent-level state that all children compare against (like `activeId`)
|
|
18
|
+
- [ ] Pre-compute derived boolean props in the parent (like `:active="item.id === activeId"`)
|
|
19
|
+
- [ ] Profile re-renders using Vue DevTools to identify prop stability issues
|
|
20
|
+
- [ ] Consider this pattern especially critical for large lists
|
|
21
|
+
|
|
22
|
+
**Incorrect:**
|
|
23
|
+
```vue
|
|
24
|
+
<template>
|
|
25
|
+
<!-- BAD: activeId changes -> ALL 100 ListItems re-render -->
|
|
26
|
+
<ListItem
|
|
27
|
+
v-for="item in list"
|
|
28
|
+
:key="item.id"
|
|
29
|
+
:id="item.id"
|
|
30
|
+
:active-id="activeId"
|
|
31
|
+
/>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script setup>
|
|
35
|
+
import { ref } from 'vue'
|
|
36
|
+
|
|
37
|
+
const list = ref([/* 100 items */])
|
|
38
|
+
const activeId = ref(null)
|
|
39
|
+
|
|
40
|
+
// When activeId changes from 1 to 2:
|
|
41
|
+
// - ListItem 1 needs to re-render (was active, now not)
|
|
42
|
+
// - ListItem 2 needs to re-render (was not active, now active)
|
|
43
|
+
// - All other 98 ListItems ALSO re-render because activeId prop changed!
|
|
44
|
+
</script>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```vue
|
|
48
|
+
<!-- ListItem.vue - receives activeId and compares internally -->
|
|
49
|
+
<template>
|
|
50
|
+
<div :class="{ active: id === activeId }">
|
|
51
|
+
{{ id }}
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<script setup>
|
|
56
|
+
defineProps({
|
|
57
|
+
id: Number,
|
|
58
|
+
activeId: Number // This prop changes for ALL items
|
|
59
|
+
})
|
|
60
|
+
</script>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Correct:**
|
|
64
|
+
```vue
|
|
65
|
+
<template>
|
|
66
|
+
<!-- GOOD: Only items whose :active actually changed will re-render -->
|
|
67
|
+
<ListItem
|
|
68
|
+
v-for="item in list"
|
|
69
|
+
:key="item.id"
|
|
70
|
+
:id="item.id"
|
|
71
|
+
:active="item.id === activeId"
|
|
72
|
+
/>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<script setup>
|
|
76
|
+
import { ref } from 'vue'
|
|
77
|
+
|
|
78
|
+
const list = ref([/* 100 items */])
|
|
79
|
+
const activeId = ref(null)
|
|
80
|
+
|
|
81
|
+
// When activeId changes from 1 to 2:
|
|
82
|
+
// - ListItem 1: :active changed from true to false -> re-renders
|
|
83
|
+
// - ListItem 2: :active changed from false to true -> re-renders
|
|
84
|
+
// - All other 98 ListItems: :active is still false -> NO re-render!
|
|
85
|
+
</script>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```vue
|
|
89
|
+
<!-- ListItem.vue - receives pre-computed boolean -->
|
|
90
|
+
<template>
|
|
91
|
+
<div :class="{ active }">
|
|
92
|
+
{{ id }}
|
|
93
|
+
</div>
|
|
94
|
+
</template>
|
|
95
|
+
|
|
96
|
+
<script setup>
|
|
97
|
+
defineProps({
|
|
98
|
+
id: Number,
|
|
99
|
+
active: Boolean // This only changes for items that truly changed
|
|
100
|
+
})
|
|
101
|
+
</script>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Common Patterns That Cause Prop Instability
|
|
105
|
+
|
|
106
|
+
```vue
|
|
107
|
+
<!-- BAD: Passing index that could shift -->
|
|
108
|
+
<Item
|
|
109
|
+
v-for="(item, index) in items"
|
|
110
|
+
:key="item.id"
|
|
111
|
+
:index="index"
|
|
112
|
+
:total="items.length" <!-- Changes when list changes -->
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
<!-- BAD: Passing entire selection set -->
|
|
116
|
+
<Item
|
|
117
|
+
v-for="item in items"
|
|
118
|
+
:key="item.id"
|
|
119
|
+
:selected-ids="selectedIds" <!-- All items re-render on any selection -->
|
|
120
|
+
/>
|
|
121
|
+
|
|
122
|
+
<!-- GOOD: Pre-compute the boolean -->
|
|
123
|
+
<Item
|
|
124
|
+
v-for="item in items"
|
|
125
|
+
:key="item.id"
|
|
126
|
+
:selected="selectedIds.includes(item.id)"
|
|
127
|
+
/>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Performance Impact Example
|
|
131
|
+
|
|
132
|
+
| Scenario | Props Changed | Components Re-rendered |
|
|
133
|
+
|----------|---------------|------------------------|
|
|
134
|
+
| 100 items, pass `activeId` | 100 | 100 (all) |
|
|
135
|
+
| 100 items, pass `:active` boolean | 2 | 2 (only changed) |
|
|
136
|
+
| 1000 items, pass `activeId` | 1000 | 1000 (all) |
|
|
137
|
+
| 1000 items, pass `:active` boolean | 2 | 2 (only changed) |
|
|
138
|
+
|
|
139
|
+
## Reference
|
|
140
|
+
- [Vue.js Performance - Props Stability](https://vuejs.org/guide/best-practices/performance.html#props-stability)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Use Global Properties Sparingly in Plugins
|
|
2
|
+
|
|
3
|
+
## Rule
|
|
4
|
+
|
|
5
|
+
When using `app.config.globalProperties` in Vue plugins, use them sparingly and with clear naming conventions. Excessive global properties lead to confusion, naming conflicts, and debugging difficulties.
|
|
6
|
+
|
|
7
|
+
## Why This Matters
|
|
8
|
+
|
|
9
|
+
1. **Implicit dependencies**: Global properties make component dependencies invisible, making code harder to understand and maintain.
|
|
10
|
+
|
|
11
|
+
2. **Naming collisions**: Multiple plugins may try to use the same property name (e.g., `$http`, `$api`), causing silent overwrites.
|
|
12
|
+
|
|
13
|
+
3. **Debugging difficulty**: When issues arise, tracing back to which plugin provides a global property is challenging.
|
|
14
|
+
|
|
15
|
+
4. **IDE limitations**: Global properties may not have proper autocomplete or type checking without careful configuration.
|
|
16
|
+
|
|
17
|
+
5. **Testing complexity**: Global state is harder to mock and isolate in unit tests.
|
|
18
|
+
|
|
19
|
+
## Bad Practice
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// Too many global properties from various plugins
|
|
23
|
+
app.config.globalProperties.$http = axios
|
|
24
|
+
app.config.globalProperties.$api = apiClient
|
|
25
|
+
app.config.globalProperties.$auth = authService
|
|
26
|
+
app.config.globalProperties.$translate = i18n.translate
|
|
27
|
+
app.config.globalProperties.$format = formatters
|
|
28
|
+
app.config.globalProperties.$utils = utilities
|
|
29
|
+
app.config.globalProperties.$config = appConfig
|
|
30
|
+
app.config.globalProperties.$logger = logger
|
|
31
|
+
|
|
32
|
+
// In component - where did all these come from?
|
|
33
|
+
export default {
|
|
34
|
+
mounted() {
|
|
35
|
+
this.$logger.info('Mounted')
|
|
36
|
+
const data = await this.$http.get(this.$config.apiUrl)
|
|
37
|
+
this.$api.process(this.$utils.transform(data))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Good Practice
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// Use provide/inject for most functionality
|
|
46
|
+
export default {
|
|
47
|
+
install(app, options) {
|
|
48
|
+
// Provide services via injection
|
|
49
|
+
app.provide('api', apiClient)
|
|
50
|
+
app.provide('auth', authService)
|
|
51
|
+
app.provide('i18n', i18n)
|
|
52
|
+
|
|
53
|
+
// Reserve globalProperties for truly global template helpers
|
|
54
|
+
// that are used extensively in templates across the app
|
|
55
|
+
app.config.globalProperties.$t = i18n.translate // Common convention
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// In component - explicit dependencies
|
|
60
|
+
<script setup>
|
|
61
|
+
import { inject } from 'vue'
|
|
62
|
+
|
|
63
|
+
const api = inject('api')
|
|
64
|
+
const auth = inject('auth')
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<template>
|
|
68
|
+
<!-- $t is acceptable for common template-only usage -->
|
|
69
|
+
<h1>{{ $t('welcome') }}</h1>
|
|
70
|
+
</template>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Naming Conventions
|
|
74
|
+
|
|
75
|
+
If you do use globalProperties:
|
|
76
|
+
|
|
77
|
+
1. **Use `$` prefix**: This is the Vue convention and avoids conflicts with component data/methods
|
|
78
|
+
2. **Use unique prefixes for your library**: e.g., `$myLib_translate` for third-party plugins
|
|
79
|
+
3. **Document all global properties**: Keep a central registry of what each plugin provides
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Good: namespaced to avoid conflicts
|
|
83
|
+
app.config.globalProperties.$myPlugin = {
|
|
84
|
+
translate: (key) => /* ... */,
|
|
85
|
+
format: (value) => /* ... */
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Usage
|
|
89
|
+
{{ $myPlugin.translate('key') }}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Auditing Global Properties
|
|
93
|
+
|
|
94
|
+
You can inspect all global properties for debugging:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
console.log(app.config.globalProperties)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## When Global Properties Are Acceptable
|
|
101
|
+
|
|
102
|
+
1. **Template-only utilities** used very frequently (like `$t` for translations)
|
|
103
|
+
2. **Legacy migration** when transitioning from Vue 2
|
|
104
|
+
3. **Libraries that need Options API compatibility** (but prefer also providing inject)
|
|
105
|
+
|
|
106
|
+
## References
|
|
107
|
+
|
|
108
|
+
- [Vue.js Plugins Documentation](https://vuejs.org/guide/reusability/plugins.html)
|
|
109
|
+
- [Vue.js Global Properties](https://vuejs.org/api/application.html#app-config-globalproperties)
|