@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,193 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Scoped CSS Does Not Apply to Dynamically Added Content
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Programmatically inserted DOM elements won't receive scoped style data attributes, causing styles to fail silently
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, sfc, scoped-css, dynamic-content, v-html]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Scoped CSS Does Not Apply to Dynamically Added Content
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - Vue's scoped CSS works by adding data attributes to elements at compile time. Content added dynamically at runtime (via `v-html`, JavaScript DOM manipulation, or third-party libraries) won't have these attributes, so scoped styles won't apply.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] For `v-html` content, use `:deep()` selectors or unscoped styles
|
|
16
|
+
- [ ] Avoid programmatic DOM manipulation; prefer Vue's reactive template system
|
|
17
|
+
- [ ] When DOM manipulation is unavoidable, use global styles with unique class prefixes
|
|
18
|
+
- [ ] Consider CSS modules for content that mixes static and dynamic elements
|
|
19
|
+
|
|
20
|
+
**Problematic Code:**
|
|
21
|
+
```vue
|
|
22
|
+
<script setup>
|
|
23
|
+
import { ref } from 'vue'
|
|
24
|
+
|
|
25
|
+
const htmlContent = ref('<p class="dynamic">This is dynamic content</p>')
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<template>
|
|
29
|
+
<div class="container">
|
|
30
|
+
<div v-html="htmlContent"></div>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<style scoped>
|
|
35
|
+
/* BAD: Won't apply to the dynamic <p> element! */
|
|
36
|
+
.dynamic {
|
|
37
|
+
color: red;
|
|
38
|
+
font-weight: bold;
|
|
39
|
+
}
|
|
40
|
+
</style>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Correct Code:**
|
|
44
|
+
```vue
|
|
45
|
+
<script setup>
|
|
46
|
+
import { ref } from 'vue'
|
|
47
|
+
|
|
48
|
+
const htmlContent = ref('<p class="dynamic">This is dynamic content</p>')
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<div class="container">
|
|
53
|
+
<div v-html="htmlContent"></div>
|
|
54
|
+
</div>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<style scoped>
|
|
58
|
+
/* GOOD: Use :deep() for v-html content */
|
|
59
|
+
.container :deep(.dynamic) {
|
|
60
|
+
color: red;
|
|
61
|
+
font-weight: bold;
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Why This Happens
|
|
67
|
+
|
|
68
|
+
Vue scoped CSS adds a unique data attribute (e.g., `data-v-7ba5bd90`) to:
|
|
69
|
+
1. All elements in the component's template (at compile time)
|
|
70
|
+
2. All CSS selectors
|
|
71
|
+
|
|
72
|
+
```html
|
|
73
|
+
<!-- What Vue generates at compile time -->
|
|
74
|
+
<div class="container" data-v-7ba5bd90>
|
|
75
|
+
<div data-v-7ba5bd90>
|
|
76
|
+
<!-- v-html content is inserted at runtime WITHOUT the attribute -->
|
|
77
|
+
<p class="dynamic">This is dynamic content</p>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```css
|
|
83
|
+
/* Generated scoped CSS */
|
|
84
|
+
.dynamic[data-v-7ba5bd90] { color: red; }
|
|
85
|
+
/* ^ Won't match because the dynamic <p> doesn't have data-v-7ba5bd90 */
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Alternative: Global Styles with Unique Prefix
|
|
89
|
+
|
|
90
|
+
```vue
|
|
91
|
+
<script setup>
|
|
92
|
+
import { ref } from 'vue'
|
|
93
|
+
const htmlContent = ref('<p class="my-component-dynamic">Dynamic text</p>')
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<template>
|
|
97
|
+
<div class="my-component">
|
|
98
|
+
<div v-html="htmlContent"></div>
|
|
99
|
+
</div>
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
<!-- Use unscoped styles with unique prefixes -->
|
|
103
|
+
<style>
|
|
104
|
+
.my-component .my-component-dynamic {
|
|
105
|
+
color: red;
|
|
106
|
+
}
|
|
107
|
+
</style>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Programmatic DOM Manipulation
|
|
111
|
+
|
|
112
|
+
When using third-party libraries that manipulate the DOM:
|
|
113
|
+
|
|
114
|
+
```vue
|
|
115
|
+
<script setup>
|
|
116
|
+
import { ref, onMounted } from 'vue'
|
|
117
|
+
|
|
118
|
+
const editorRef = ref(null)
|
|
119
|
+
|
|
120
|
+
onMounted(() => {
|
|
121
|
+
// Third-party editor that injects its own DOM elements
|
|
122
|
+
initRichEditor(editorRef.value)
|
|
123
|
+
})
|
|
124
|
+
</script>
|
|
125
|
+
|
|
126
|
+
<template>
|
|
127
|
+
<div class="editor-wrapper">
|
|
128
|
+
<div ref="editorRef"></div>
|
|
129
|
+
</div>
|
|
130
|
+
</template>
|
|
131
|
+
|
|
132
|
+
<style scoped>
|
|
133
|
+
/* BAD: Won't reach injected editor elements */
|
|
134
|
+
.editor-toolbar { ... }
|
|
135
|
+
.editor-content { ... }
|
|
136
|
+
</style>
|
|
137
|
+
|
|
138
|
+
<style>
|
|
139
|
+
/* GOOD: Global styles scoped by parent class */
|
|
140
|
+
.editor-wrapper .editor-toolbar {
|
|
141
|
+
background: #f5f5f5;
|
|
142
|
+
}
|
|
143
|
+
.editor-wrapper .editor-content {
|
|
144
|
+
padding: 1rem;
|
|
145
|
+
}
|
|
146
|
+
</style>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Best Practice: Prefer Reactive Templates
|
|
150
|
+
|
|
151
|
+
Instead of dynamic HTML, use Vue's reactive system when possible:
|
|
152
|
+
|
|
153
|
+
```vue
|
|
154
|
+
<script setup>
|
|
155
|
+
import { ref } from 'vue'
|
|
156
|
+
|
|
157
|
+
// BAD: Dynamic HTML that needs special style handling
|
|
158
|
+
const badHtml = ref('<span class="highlight">text</span>')
|
|
159
|
+
|
|
160
|
+
// GOOD: Reactive data that templates handle
|
|
161
|
+
const items = ref([
|
|
162
|
+
{ text: 'Item 1', isHighlighted: true },
|
|
163
|
+
{ text: 'Item 2', isHighlighted: false }
|
|
164
|
+
])
|
|
165
|
+
</script>
|
|
166
|
+
|
|
167
|
+
<template>
|
|
168
|
+
<!-- BAD -->
|
|
169
|
+
<div v-html="badHtml"></div>
|
|
170
|
+
|
|
171
|
+
<!-- GOOD: Scoped styles work normally -->
|
|
172
|
+
<ul>
|
|
173
|
+
<li
|
|
174
|
+
v-for="item in items"
|
|
175
|
+
:key="item.text"
|
|
176
|
+
:class="{ highlight: item.isHighlighted }"
|
|
177
|
+
>
|
|
178
|
+
{{ item.text }}
|
|
179
|
+
</li>
|
|
180
|
+
</ul>
|
|
181
|
+
</template>
|
|
182
|
+
|
|
183
|
+
<style scoped>
|
|
184
|
+
/* Works perfectly with reactive template */
|
|
185
|
+
.highlight {
|
|
186
|
+
background: yellow;
|
|
187
|
+
}
|
|
188
|
+
</style>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Reference
|
|
192
|
+
- [Vue.js Scoped CSS](https://vuejs.org/api/sfc-css-features.html#scoped-css)
|
|
193
|
+
- [GitHub Issue: Scoped CSS not applied for programmatically added elements](https://github.com/vuejs/vue/issues/7649)
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Scoped CSS Cannot Style Slot Content Directly
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Slot content receives the parent component's scope, not the child's, causing styles to fail unexpectedly
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, sfc, scoped-css, slots, deep-selector]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Scoped CSS Cannot Style Slot Content Directly
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - When a parent passes content through a slot, that content receives the parent component's scoped style attributes, not the child component's. This means the child component cannot style slot content with regular scoped CSS.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Use `:deep()` selector in the wrapper component to style slot content
|
|
16
|
+
- [ ] Alternatively, use `:slotted()` pseudo-selector to target slotted elements
|
|
17
|
+
- [ ] For complex slot styling, consider using CSS modules or unscoped styles
|
|
18
|
+
- [ ] Document expected slot content structure when styling assumptions exist
|
|
19
|
+
|
|
20
|
+
**Problematic Code:**
|
|
21
|
+
```vue
|
|
22
|
+
<!-- Card.vue (child component) -->
|
|
23
|
+
<template>
|
|
24
|
+
<div class="card">
|
|
25
|
+
<div class="card-body">
|
|
26
|
+
<slot />
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<style scoped>
|
|
32
|
+
.card-body {
|
|
33
|
+
padding: 1rem;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* BAD: Won't apply to slot content! */
|
|
37
|
+
.card-body h2 {
|
|
38
|
+
color: #333;
|
|
39
|
+
margin-bottom: 0.5rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.card-body p {
|
|
43
|
+
color: #666;
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```vue
|
|
49
|
+
<!-- Parent.vue -->
|
|
50
|
+
<template>
|
|
51
|
+
<Card>
|
|
52
|
+
<!-- This h2 and p won't be styled by Card's scoped CSS -->
|
|
53
|
+
<h2>Card Title</h2>
|
|
54
|
+
<p>Card description text.</p>
|
|
55
|
+
</Card>
|
|
56
|
+
</template>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Correct Code:**
|
|
60
|
+
```vue
|
|
61
|
+
<!-- Card.vue - Using :slotted() -->
|
|
62
|
+
<template>
|
|
63
|
+
<div class="card">
|
|
64
|
+
<div class="card-body">
|
|
65
|
+
<slot />
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</template>
|
|
69
|
+
|
|
70
|
+
<style scoped>
|
|
71
|
+
.card-body {
|
|
72
|
+
padding: 1rem;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* GOOD: :slotted() targets slot content */
|
|
76
|
+
:slotted(h2) {
|
|
77
|
+
color: #333;
|
|
78
|
+
margin-bottom: 0.5rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
:slotted(p) {
|
|
82
|
+
color: #666;
|
|
83
|
+
}
|
|
84
|
+
</style>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Using :deep() Alternative
|
|
88
|
+
|
|
89
|
+
```vue
|
|
90
|
+
<!-- Card.vue - Using :deep() -->
|
|
91
|
+
<style scoped>
|
|
92
|
+
.card-body {
|
|
93
|
+
padding: 1rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* :deep() also works for slot content */
|
|
97
|
+
.card-body :deep(h2) {
|
|
98
|
+
color: #333;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.card-body :deep(p) {
|
|
102
|
+
color: #666;
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Why This Happens
|
|
108
|
+
|
|
109
|
+
Slot content is compiled in the parent component's scope:
|
|
110
|
+
|
|
111
|
+
```vue
|
|
112
|
+
<!-- Parent template compiles to: -->
|
|
113
|
+
<Card>
|
|
114
|
+
<h2 data-v-parent123>Card Title</h2>
|
|
115
|
+
<p data-v-parent123>Card description</p>
|
|
116
|
+
</Card>
|
|
117
|
+
|
|
118
|
+
<!-- Card template compiles to: -->
|
|
119
|
+
<div class="card" data-v-card456>
|
|
120
|
+
<div class="card-body" data-v-card456>
|
|
121
|
+
<slot /> <!-- Content inserted WITHOUT data-v-card456 -->
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The `<h2>` has `data-v-parent123`, but Card's scoped CSS expects `data-v-card456`.
|
|
127
|
+
|
|
128
|
+
## :slotted() vs :deep() for Slots
|
|
129
|
+
|
|
130
|
+
Both work, but have subtle differences:
|
|
131
|
+
|
|
132
|
+
```vue
|
|
133
|
+
<style scoped>
|
|
134
|
+
/* :slotted() - Specifically for slot content */
|
|
135
|
+
/* Only targets direct slotted elements */
|
|
136
|
+
:slotted(h2) {
|
|
137
|
+
color: blue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* :deep() - More general deep selector */
|
|
141
|
+
/* Can target nested elements within slot content */
|
|
142
|
+
.card-body :deep(h2) {
|
|
143
|
+
color: blue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* For nested elements in slot content, must use :deep() */
|
|
147
|
+
:slotted(.wrapper h2) { } /* Won't work for nested h2 */
|
|
148
|
+
.card-body :deep(.wrapper h2) { } /* Works for nested */
|
|
149
|
+
</style>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Combining with Named Slots
|
|
153
|
+
|
|
154
|
+
```vue
|
|
155
|
+
<template>
|
|
156
|
+
<div class="card">
|
|
157
|
+
<header class="card-header">
|
|
158
|
+
<slot name="header" />
|
|
159
|
+
</header>
|
|
160
|
+
<div class="card-body">
|
|
161
|
+
<slot />
|
|
162
|
+
</div>
|
|
163
|
+
<footer class="card-footer">
|
|
164
|
+
<slot name="footer" />
|
|
165
|
+
</footer>
|
|
166
|
+
</div>
|
|
167
|
+
</template>
|
|
168
|
+
|
|
169
|
+
<style scoped>
|
|
170
|
+
/* Style specific slot content */
|
|
171
|
+
.card-header :slotted(h1),
|
|
172
|
+
.card-header :slotted(h2) {
|
|
173
|
+
margin: 0;
|
|
174
|
+
font-size: 1.25rem;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.card-body :slotted(p) {
|
|
178
|
+
margin-bottom: 1rem;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.card-footer :slotted(button) {
|
|
182
|
+
margin-right: 0.5rem;
|
|
183
|
+
}
|
|
184
|
+
</style>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Performance Tip: Use Classes
|
|
188
|
+
|
|
189
|
+
Element selectors with `:slotted()` can be slower:
|
|
190
|
+
|
|
191
|
+
```vue
|
|
192
|
+
<style scoped>
|
|
193
|
+
/* SLOWER: Element selector */
|
|
194
|
+
:slotted(p) {
|
|
195
|
+
color: gray;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* FASTER: Class selector */
|
|
199
|
+
:slotted(.card-text) {
|
|
200
|
+
color: gray;
|
|
201
|
+
}
|
|
202
|
+
</style>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## When to Use Unscoped Styles
|
|
206
|
+
|
|
207
|
+
For complex slot styling, unscoped styles may be cleaner:
|
|
208
|
+
|
|
209
|
+
```vue
|
|
210
|
+
<template>
|
|
211
|
+
<article class="article-card">
|
|
212
|
+
<slot />
|
|
213
|
+
</article>
|
|
214
|
+
</template>
|
|
215
|
+
|
|
216
|
+
<style>
|
|
217
|
+
/* Unscoped with unique prefix for complex content styling */
|
|
218
|
+
.article-card h1,
|
|
219
|
+
.article-card h2,
|
|
220
|
+
.article-card h3 {
|
|
221
|
+
font-family: Georgia, serif;
|
|
222
|
+
line-height: 1.2;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.article-card p {
|
|
226
|
+
line-height: 1.6;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.article-card img {
|
|
230
|
+
max-width: 100%;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.article-card blockquote {
|
|
234
|
+
border-left: 3px solid #ccc;
|
|
235
|
+
padding-left: 1rem;
|
|
236
|
+
}
|
|
237
|
+
</style>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Reference
|
|
241
|
+
- [Vue.js Scoped CSS - Slotted Selectors](https://vuejs.org/api/sfc-css-features.html#slotted-selectors)
|
|
242
|
+
- [Vue.js Deep Selectors](https://vuejs.org/api/sfc-css-features.html#deep-selectors)
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Variables in Script Setup Are Not Reactive by Default
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Forgetting to wrap variables with ref() or reactive() causes silent reactivity failures in script setup
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, sfc, script-setup, reactivity, ref, composition-api]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Variables in Script Setup Are Not Reactive by Default
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - Unlike Options API's `data()` which automatically makes properties reactive, variables declared in `<script setup>` are plain JavaScript values. You must explicitly use `ref()` or `reactive()` to make them reactive. Forgetting this causes the UI to not update when values change.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Always wrap primitive values (strings, numbers, booleans) with `ref()`
|
|
16
|
+
- [ ] Use `reactive()` for objects when you don't need to reassign the whole object
|
|
17
|
+
- [ ] Remember to access `.value` on refs in script (not needed in templates)
|
|
18
|
+
- [ ] Use `computed()` from Vue, not a plain function, for derived reactive state
|
|
19
|
+
|
|
20
|
+
**Problematic Code:**
|
|
21
|
+
```vue
|
|
22
|
+
<script setup>
|
|
23
|
+
// BAD: These are NOT reactive!
|
|
24
|
+
let count = 0
|
|
25
|
+
let message = 'Hello'
|
|
26
|
+
let user = { name: 'John', age: 30 }
|
|
27
|
+
|
|
28
|
+
function increment() {
|
|
29
|
+
count++ // This change won't update the UI!
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function updateMessage() {
|
|
33
|
+
message = 'World' // UI won't reflect this change!
|
|
34
|
+
}
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
<template>
|
|
38
|
+
<div>
|
|
39
|
+
<!-- Will always show initial values -->
|
|
40
|
+
<p>Count: {{ count }}</p>
|
|
41
|
+
<p>Message: {{ message }}</p>
|
|
42
|
+
<button @click="increment">Increment</button>
|
|
43
|
+
<button @click="updateMessage">Update</button>
|
|
44
|
+
</div>
|
|
45
|
+
</template>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Correct Code:**
|
|
49
|
+
```vue
|
|
50
|
+
<script setup>
|
|
51
|
+
import { ref, reactive, computed } from 'vue'
|
|
52
|
+
|
|
53
|
+
// GOOD: Primitives wrapped with ref()
|
|
54
|
+
const count = ref(0)
|
|
55
|
+
const message = ref('Hello')
|
|
56
|
+
|
|
57
|
+
// GOOD: Object with reactive()
|
|
58
|
+
const user = reactive({ name: 'John', age: 30 })
|
|
59
|
+
|
|
60
|
+
// GOOD: Computed for derived state
|
|
61
|
+
const doubleCount = computed(() => count.value * 2)
|
|
62
|
+
|
|
63
|
+
function increment() {
|
|
64
|
+
count.value++ // Use .value for refs in script
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function updateMessage() {
|
|
68
|
+
message.value = 'World'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function updateUser() {
|
|
72
|
+
user.name = 'Jane' // No .value needed for reactive objects
|
|
73
|
+
}
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<template>
|
|
77
|
+
<div>
|
|
78
|
+
<!-- No .value needed in templates - Vue unwraps automatically -->
|
|
79
|
+
<p>Count: {{ count }}</p>
|
|
80
|
+
<p>Double: {{ doubleCount }}</p>
|
|
81
|
+
<p>Message: {{ message }}</p>
|
|
82
|
+
<p>User: {{ user.name }}</p>
|
|
83
|
+
<button @click="increment">Increment</button>
|
|
84
|
+
</div>
|
|
85
|
+
</template>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Common Mistake: Plain Computed
|
|
89
|
+
|
|
90
|
+
```vue
|
|
91
|
+
<script setup>
|
|
92
|
+
import { ref } from 'vue'
|
|
93
|
+
|
|
94
|
+
const items = ref([1, 2, 3, 4, 5])
|
|
95
|
+
|
|
96
|
+
// BAD: Plain function, not reactive - won't update when items change
|
|
97
|
+
const total = items.value.reduce((sum, n) => sum + n, 0)
|
|
98
|
+
|
|
99
|
+
// BAD: Arrow function - recalculates but Vue doesn't track it
|
|
100
|
+
const getTotal = () => items.value.reduce((sum, n) => sum + n, 0)
|
|
101
|
+
</script>
|
|
102
|
+
|
|
103
|
+
<template>
|
|
104
|
+
<!-- total never updates, getTotal works but isn't optimal -->
|
|
105
|
+
<p>Total: {{ total }}</p>
|
|
106
|
+
</template>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```vue
|
|
110
|
+
<script setup>
|
|
111
|
+
import { ref, computed } from 'vue'
|
|
112
|
+
|
|
113
|
+
const items = ref([1, 2, 3, 4, 5])
|
|
114
|
+
|
|
115
|
+
// GOOD: computed() tracks dependencies and caches result
|
|
116
|
+
const total = computed(() => items.value.reduce((sum, n) => sum + n, 0))
|
|
117
|
+
</script>
|
|
118
|
+
|
|
119
|
+
<template>
|
|
120
|
+
<p>Total: {{ total }}</p> <!-- Updates when items change -->
|
|
121
|
+
</template>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## When to Use ref() vs reactive()
|
|
125
|
+
|
|
126
|
+
```vue
|
|
127
|
+
<script setup>
|
|
128
|
+
import { ref, reactive } from 'vue'
|
|
129
|
+
|
|
130
|
+
// Use ref() for:
|
|
131
|
+
// - Primitives (string, number, boolean)
|
|
132
|
+
// - Values you might reassign entirely
|
|
133
|
+
const count = ref(0)
|
|
134
|
+
const isLoading = ref(false)
|
|
135
|
+
const selectedId = ref<number | null>(null)
|
|
136
|
+
|
|
137
|
+
// Use reactive() for:
|
|
138
|
+
// - Objects/arrays you'll mutate but not reassign
|
|
139
|
+
// - When you want to avoid .value
|
|
140
|
+
const form = reactive({
|
|
141
|
+
name: '',
|
|
142
|
+
email: '',
|
|
143
|
+
errors: []
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
// Gotcha: Can't reassign reactive objects
|
|
147
|
+
const user = reactive({ name: 'John' })
|
|
148
|
+
// user = { name: 'Jane' } // This breaks reactivity!
|
|
149
|
+
// user.name = 'Jane' // This works
|
|
150
|
+
|
|
151
|
+
// Use ref() if you need to reassign objects
|
|
152
|
+
const userData = ref({ name: 'John' })
|
|
153
|
+
userData.value = { name: 'Jane' } // This works
|
|
154
|
+
</script>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Template Automatic Unwrapping
|
|
158
|
+
|
|
159
|
+
Vue automatically unwraps refs in templates:
|
|
160
|
+
|
|
161
|
+
```vue
|
|
162
|
+
<script setup>
|
|
163
|
+
import { ref } from 'vue'
|
|
164
|
+
|
|
165
|
+
const count = ref(0)
|
|
166
|
+
const user = ref({ name: 'John' })
|
|
167
|
+
</script>
|
|
168
|
+
|
|
169
|
+
<template>
|
|
170
|
+
<!-- All of these work - no .value needed -->
|
|
171
|
+
<p>{{ count }}</p>
|
|
172
|
+
<p>{{ user.name }}</p>
|
|
173
|
+
<input v-model="count" type="number">
|
|
174
|
+
<button @click="count++">Increment</button>
|
|
175
|
+
</template>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
But in event handlers written inline, you might still need `.value`:
|
|
179
|
+
|
|
180
|
+
```vue
|
|
181
|
+
<template>
|
|
182
|
+
<!-- This works (Vue handles it) -->
|
|
183
|
+
<button @click="count++">+1</button>
|
|
184
|
+
|
|
185
|
+
<!-- For complex logic, .value may be needed -->
|
|
186
|
+
<button @click="() => { count.value = Math.max(0, count.value - 1) }">
|
|
187
|
+
-1 (min 0)
|
|
188
|
+
</button>
|
|
189
|
+
</template>
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Reference
|
|
193
|
+
- [Vue.js Reactivity Fundamentals](https://vuejs.org/guide/essentials/reactivity-fundamentals.html)
|
|
194
|
+
- [Vue.js ref()](https://vuejs.org/api/reactivity-core.html#ref)
|
|
195
|
+
- [Vue.js reactive()](https://vuejs.org/api/reactivity-core.html#reactive)
|