agy-superpowers 5.0.8 → 5.0.9
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/package.json +1 -1
- package/template/agent/rules/superpowers.md +54 -0
- package/template/agent/skills/frontend-developer/SKILL.md +39 -0
- package/template/agent/skills/frontend-developer/references/react-nextjs.md +343 -0
- package/template/agent/skills/frontend-developer/references/react-rules/_sections.md +46 -0
- package/template/agent/skills/frontend-developer/references/react-rules/_template.md +28 -0
- package/template/agent/skills/frontend-developer/references/react-rules/advanced-event-handler-refs.md +55 -0
- package/template/agent/skills/frontend-developer/references/react-rules/advanced-init-once.md +42 -0
- package/template/agent/skills/frontend-developer/references/react-rules/advanced-use-latest.md +39 -0
- package/template/agent/skills/frontend-developer/references/react-rules/async-api-routes.md +38 -0
- package/template/agent/skills/frontend-developer/references/react-rules/async-defer-await.md +80 -0
- package/template/agent/skills/frontend-developer/references/react-rules/async-dependencies.md +51 -0
- package/template/agent/skills/frontend-developer/references/react-rules/async-parallel.md +28 -0
- package/template/agent/skills/frontend-developer/references/react-rules/async-suspense-boundaries.md +99 -0
- package/template/agent/skills/frontend-developer/references/react-rules/bundle-barrel-imports.md +59 -0
- package/template/agent/skills/frontend-developer/references/react-rules/bundle-conditional.md +31 -0
- package/template/agent/skills/frontend-developer/references/react-rules/bundle-defer-third-party.md +49 -0
- package/template/agent/skills/frontend-developer/references/react-rules/bundle-dynamic-imports.md +35 -0
- package/template/agent/skills/frontend-developer/references/react-rules/bundle-preload.md +50 -0
- package/template/agent/skills/frontend-developer/references/react-rules/client-event-listeners.md +74 -0
- package/template/agent/skills/frontend-developer/references/react-rules/client-localstorage-schema.md +71 -0
- package/template/agent/skills/frontend-developer/references/react-rules/client-passive-event-listeners.md +48 -0
- package/template/agent/skills/frontend-developer/references/react-rules/client-swr-dedup.md +56 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-batch-dom-css.md +107 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-cache-function-results.md +80 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-cache-property-access.md +28 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-cache-storage.md +70 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-combine-iterations.md +32 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-early-exit.md +50 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-flatmap-filter.md +60 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-hoist-regexp.md +45 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-index-maps.md +37 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-length-check-first.md +49 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-min-max-loop.md +82 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-set-map-lookups.md +24 -0
- package/template/agent/skills/frontend-developer/references/react-rules/js-tosorted-immutable.md +57 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-activity.md +26 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-animate-svg-wrapper.md +47 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-conditional-render.md +40 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-content-visibility.md +38 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-hoist-jsx.md +46 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-hydration-no-flicker.md +82 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-hydration-suppress-warning.md +30 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-resource-hints.md +85 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-script-defer-async.md +68 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-svg-precision.md +28 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rendering-usetransition-loading.md +75 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-defer-reads.md +39 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-dependencies.md +45 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-derived-state-no-effect.md +40 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-derived-state.md +29 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-functional-setstate.md +74 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-lazy-state-init.md +58 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-memo-with-default-value.md +38 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-memo.md +44 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-move-effect-to-event.md +45 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-no-inline-components.md +82 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-simple-expression-in-memo.md +35 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-split-combined-hooks.md +64 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-transitions.md +40 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-use-deferred-value.md +59 -0
- package/template/agent/skills/frontend-developer/references/react-rules/rerender-use-ref-transient-values.md +73 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-after-nonblocking.md +73 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-auth-actions.md +96 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-cache-lru.md +41 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-cache-react.md +76 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-dedup-props.md +65 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-hoist-static-io.md +142 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-parallel-fetching.md +83 -0
- package/template/agent/skills/frontend-developer/references/react-rules/server-serialization.md +38 -0
- package/template/agent/skills/frontend-developer/references/svelte-sveltekit.md +220 -0
- package/template/agent/skills/frontend-developer/references/vanilla.md +275 -0
- package/template/agent/skills/frontend-developer/references/vue-nuxt.md +289 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/_index.md +154 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/animation-class-based-technique.md +254 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/animation-state-driven-technique.md +291 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-async.md +97 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-data-flow.md +307 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-fallthrough-attrs.md +174 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-keep-alive.md +137 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-slots.md +216 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-suspense.md +228 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-teleport.md +108 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-transition-group.md +128 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/component-transition.md +125 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/composables.md +290 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/directives.md +162 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/perf-avoid-component-abstraction-in-lists.md +159 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/perf-v-once-v-memo-directives.md +182 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/perf-virtualize-large-lists.md +187 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/plugins.md +166 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/reactivity.md +344 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/render-functions.md +201 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/sfc.md +310 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/state-management.md +135 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/best-practices/updated-hook-performance.md +187 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/_index.md +23 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-beforeenter-no-param-trigger.md +167 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-beforerouteenter-no-this.md +176 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-guard-async-await-pattern.md +227 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-navigation-guard-infinite-loop.md +187 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-navigation-guard-next-deprecated.md +150 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-param-change-no-lifecycle.md +181 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-simple-routing-cleanup.md +209 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/router/router-use-vue-router-for-production.md +183 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/_index.md +29 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/async-component-testing.md +163 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/teleport-testing-complexity.md +158 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-async-await-flushpromises.md +175 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-browser-vs-node-runners.md +208 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-component-blackbox-approach.md +144 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-composables-helper-wrapper.md +238 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-e2e-playwright-recommended.md +242 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-no-snapshot-only.md +197 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-pinia-store-setup.md +228 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-suspense-async-components.md +229 -0
- package/template/agent/skills/frontend-developer/references/vue-rules/testing/testing-vitest-recommended-for-vue.md +204 -0
- package/template/agent/skills/mobile-developer/SKILL.md +52 -0
- package/template/agent/skills/mobile-developer/references/android-native.md +396 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-accessibility.md +36 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-architecture.md +52 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-coroutines.md +139 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-data-layer.md +51 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-emulator-skill.md +108 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-gradle-logic.md +126 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-retrofit.md +142 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-testing.md +102 -0
- package/template/agent/skills/mobile-developer/references/android-rules/android-viewmodel.md +43 -0
- package/template/agent/skills/mobile-developer/references/android-rules/coil-compose.md +74 -0
- package/template/agent/skills/mobile-developer/references/android-rules/compose-navigation.md +422 -0
- package/template/agent/skills/mobile-developer/references/android-rules/compose-performance-audit.md +199 -0
- package/template/agent/skills/mobile-developer/references/android-rules/compose-ui.md +49 -0
- package/template/agent/skills/mobile-developer/references/android-rules/gradle-build-performance.md +346 -0
- package/template/agent/skills/mobile-developer/references/android-rules/kotlin-concurrency-expert.md +169 -0
- package/template/agent/skills/mobile-developer/references/android-rules/rxjava-to-coroutines-migration.md +101 -0
- package/template/agent/skills/mobile-developer/references/android-rules/xml-to-compose-migration.md +338 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-best-practices.md +52 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-checks-migration.md +134 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-cli-app-best-practices.md +123 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-doc-validation.md +45 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-matcher-best-practices.md +106 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-modern-features.md +241 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-package-maintenance.md +75 -0
- package/template/agent/skills/mobile-developer/references/flutter-rules/dart-test-fundamentals.md +124 -0
- package/template/agent/skills/mobile-developer/references/flutter.md +291 -0
- package/template/agent/skills/mobile-developer/references/ios-native.md +358 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/accessibility-patterns.md +215 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/animation-advanced.md +403 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/animation-basics.md +284 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/animation-transitions.md +326 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/charts-accessibility.md +135 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/charts.md +602 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/image-optimization.md +203 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/latest-apis.md +464 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/layout-best-practices.md +266 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/liquid-glass.md +416 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/list-patterns.md +394 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/macos-scenes.md +318 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/macos-views.md +357 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/macos-window-styling.md +303 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/performance-patterns.md +403 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/scroll-patterns.md +293 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/sheet-navigation-patterns.md +363 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/state-management.md +417 -0
- package/template/agent/skills/mobile-developer/references/ios-rules/view-structure.md +389 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/_sections.md +86 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/_template.md +28 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/animation-derived-value.md +53 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/animation-gesture-detector-press.md +95 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/animation-gpu-properties.md +65 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/design-system-compound-components.md +66 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/fonts-config-plugin.md +71 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/imports-design-system-folder.md +68 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/js-hoist-intl.md +61 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-callbacks.md +44 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-function-references.md +132 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-images.md +53 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-inline-objects.md +97 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-item-expensive.md +94 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-item-memo.md +82 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-item-types.md +104 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/list-performance-virtualize.md +67 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/monorepo-native-deps-in-app.md +46 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/monorepo-single-dependency-versions.md +63 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/navigation-native-navigators.md +188 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/react-compiler-destructure-functions.md +50 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/react-compiler-reanimated-shared-values.md +48 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/react-state-dispatcher.md +91 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/react-state-fallback.md +56 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/react-state-minimize.md +65 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/rendering-no-falsy-and.md +74 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/rendering-text-in-text-component.md +36 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/scroll-position-no-state.md +82 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/state-ground-truth.md +80 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-expo-image.md +66 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-image-gallery.md +104 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-measure-views.md +78 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-menus.md +174 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-native-modals.md +77 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-pressable.md +61 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-safe-area-scroll.md +65 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-scrollview-content-inset.md +45 -0
- package/template/agent/skills/mobile-developer/references/react-native-rules/ui-styling.md +87 -0
- package/template/agent/skills/mobile-developer/references/react-native.md +345 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Component Fallthrough Attributes Best Practices
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Incorrect $attrs access and reactivity assumptions can cause undefined values and watchers that never run
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, attrs, fallthrough-attributes, composition-api, reactivity]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Component Fallthrough Attributes Best Practices
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Fallthrough attributes are straightforward once you follow Vue's conventions: hyphenated names use bracket notation, listener keys are camelCase `onX`, and `useAttrs()` is current-but-not-reactive.
|
|
12
|
+
|
|
13
|
+
## Task List
|
|
14
|
+
|
|
15
|
+
- Access hyphenated attribute names with bracket notation (for example `attrs['data-testid']`)
|
|
16
|
+
- Access event listeners with camelCase `onX` keys (for example `attrs.onClick`)
|
|
17
|
+
- Do not `watch()` values returned from `useAttrs()`; those watchers do not trigger on attr changes
|
|
18
|
+
- Use `onUpdated()` for attr-driven side effects
|
|
19
|
+
- Promote frequently observed attrs to props when reactive observation is required
|
|
20
|
+
|
|
21
|
+
## Access Attribute and Listener Keys Correctly
|
|
22
|
+
|
|
23
|
+
Hyphenated attribute names preserve their original casing in JavaScript, so dot notation does not work for keys that include `-`.
|
|
24
|
+
|
|
25
|
+
**BAD:**
|
|
26
|
+
```vue
|
|
27
|
+
<script setup>
|
|
28
|
+
import { useAttrs } from 'vue'
|
|
29
|
+
|
|
30
|
+
const attrs = useAttrs()
|
|
31
|
+
|
|
32
|
+
console.log(attrs.data-testid) // Syntax error
|
|
33
|
+
console.log(attrs.dataTestid) // undefined for data-testid
|
|
34
|
+
console.log(attrs['on-click']) // undefined
|
|
35
|
+
console.log(attrs['@click']) // undefined
|
|
36
|
+
</script>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**GOOD:**
|
|
40
|
+
```vue
|
|
41
|
+
<script setup>
|
|
42
|
+
import { useAttrs } from 'vue'
|
|
43
|
+
|
|
44
|
+
const attrs = useAttrs()
|
|
45
|
+
|
|
46
|
+
console.log(attrs['data-testid'])
|
|
47
|
+
console.log(attrs['aria-label'])
|
|
48
|
+
console.log(attrs['foo-bar'])
|
|
49
|
+
|
|
50
|
+
console.log(attrs.onClick)
|
|
51
|
+
console.log(attrs.onCustomEvent)
|
|
52
|
+
console.log(attrs.onMouseEnter)
|
|
53
|
+
</script>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Naming Reference
|
|
57
|
+
|
|
58
|
+
| Parent Usage | Access in `attrs` |
|
|
59
|
+
|--------------|-------------------|
|
|
60
|
+
| `class="foo"` | `attrs.class` |
|
|
61
|
+
| `data-id="123"` | `attrs['data-id']` |
|
|
62
|
+
| `aria-label="..."` | `attrs['aria-label']` |
|
|
63
|
+
| `foo-bar="baz"` | `attrs['foo-bar']` |
|
|
64
|
+
| `@click="fn"` | `attrs.onClick` |
|
|
65
|
+
| `@custom-event="fn"` | `attrs.onCustomEvent` |
|
|
66
|
+
| `@update:modelValue="fn"` | `attrs['onUpdate:modelValue']` |
|
|
67
|
+
|
|
68
|
+
## `useAttrs()` Is Not Reactive
|
|
69
|
+
|
|
70
|
+
`useAttrs()` always reflects the latest values, but it is intentionally not reactive for watcher tracking.
|
|
71
|
+
|
|
72
|
+
**BAD:**
|
|
73
|
+
```vue
|
|
74
|
+
<script setup>
|
|
75
|
+
import { watch, watchEffect, useAttrs } from 'vue'
|
|
76
|
+
|
|
77
|
+
const attrs = useAttrs()
|
|
78
|
+
|
|
79
|
+
watch(
|
|
80
|
+
() => attrs.someAttr,
|
|
81
|
+
(newValue) => {
|
|
82
|
+
console.log('Changed:', newValue) // Never runs on attr changes
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
watchEffect(() => {
|
|
87
|
+
console.log(attrs.class) // Runs on setup, not on attr updates
|
|
88
|
+
})
|
|
89
|
+
</script>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**GOOD:**
|
|
93
|
+
```vue
|
|
94
|
+
<script setup>
|
|
95
|
+
import { onUpdated, useAttrs } from 'vue'
|
|
96
|
+
|
|
97
|
+
const attrs = useAttrs()
|
|
98
|
+
|
|
99
|
+
onUpdated(() => {
|
|
100
|
+
console.log('Latest attrs:', attrs)
|
|
101
|
+
})
|
|
102
|
+
</script>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**GOOD:**
|
|
106
|
+
```vue
|
|
107
|
+
<script setup>
|
|
108
|
+
import { watch } from 'vue'
|
|
109
|
+
|
|
110
|
+
const props = defineProps({
|
|
111
|
+
someAttr: String
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
watch(
|
|
115
|
+
() => props.someAttr,
|
|
116
|
+
(newValue) => {
|
|
117
|
+
console.log('Changed:', newValue)
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
</script>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Common Patterns
|
|
124
|
+
|
|
125
|
+
### Check for optional attrs safely
|
|
126
|
+
|
|
127
|
+
```vue
|
|
128
|
+
<script setup>
|
|
129
|
+
import { computed, useAttrs } from 'vue'
|
|
130
|
+
|
|
131
|
+
const attrs = useAttrs()
|
|
132
|
+
|
|
133
|
+
const hasTestId = computed(() => 'data-testid' in attrs)
|
|
134
|
+
const ariaLabel = computed(() => attrs['aria-label'] ?? 'Default label')
|
|
135
|
+
</script>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Forward listeners after internal logic
|
|
139
|
+
|
|
140
|
+
```vue
|
|
141
|
+
<script setup>
|
|
142
|
+
import { useAttrs } from 'vue'
|
|
143
|
+
|
|
144
|
+
defineOptions({ inheritAttrs: false })
|
|
145
|
+
|
|
146
|
+
const attrs = useAttrs()
|
|
147
|
+
|
|
148
|
+
function handleClick(event) {
|
|
149
|
+
console.log('Internal handling first')
|
|
150
|
+
attrs.onClick?.(event)
|
|
151
|
+
}
|
|
152
|
+
</script>
|
|
153
|
+
|
|
154
|
+
<template>
|
|
155
|
+
<button @click="handleClick">
|
|
156
|
+
<slot />
|
|
157
|
+
</button>
|
|
158
|
+
</template>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## TypeScript Notes
|
|
162
|
+
|
|
163
|
+
`useAttrs()` is typed as `Record<string, unknown>`, so cast individual keys when needed.
|
|
164
|
+
|
|
165
|
+
```vue
|
|
166
|
+
<script setup lang="ts">
|
|
167
|
+
import { useAttrs } from 'vue'
|
|
168
|
+
|
|
169
|
+
const attrs = useAttrs()
|
|
170
|
+
|
|
171
|
+
const testId = attrs['data-testid'] as string | undefined
|
|
172
|
+
const onClick = attrs.onClick as ((event: MouseEvent) => void) | undefined
|
|
173
|
+
</script>
|
|
174
|
+
```
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: KeepAlive Component Best Practices
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: KeepAlive caches component instances; misuse causes stale data, memory growth, or unexpected lifecycle behavior
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, keepalive, cache, performance, router, dynamic-components]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# KeepAlive Component Best Practices
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - `<KeepAlive>` caches component instances instead of destroying them. Use it to preserve state across switches, but manage cache size and freshness explicitly to avoid memory growth or stale UI.
|
|
12
|
+
|
|
13
|
+
## Task List
|
|
14
|
+
|
|
15
|
+
- Use KeepAlive only where state preservation improves UX
|
|
16
|
+
- Set a reasonable `max` to cap cache size
|
|
17
|
+
- Declare component names for include/exclude matching
|
|
18
|
+
- Use `onActivated`/`onDeactivated` for cache-aware logic
|
|
19
|
+
- Decide how and when cached views refresh their data
|
|
20
|
+
- Avoid caching memory-heavy or security-sensitive views
|
|
21
|
+
|
|
22
|
+
## When to Use KeepAlive
|
|
23
|
+
|
|
24
|
+
Use KeepAlive when switching between views where state should persist (tabs, multi-step forms, dashboards). Avoid it when each visit should start fresh.
|
|
25
|
+
|
|
26
|
+
**BAD:**
|
|
27
|
+
```vue
|
|
28
|
+
<template>
|
|
29
|
+
<!-- State resets on every switch -->
|
|
30
|
+
<component :is="currentTab" />
|
|
31
|
+
</template>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**GOOD:**
|
|
35
|
+
```vue
|
|
36
|
+
<template>
|
|
37
|
+
<!-- State preserved between switches -->
|
|
38
|
+
<KeepAlive>
|
|
39
|
+
<component :is="currentTab" />
|
|
40
|
+
</KeepAlive>
|
|
41
|
+
</template>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## When NOT to Use KeepAlive
|
|
45
|
+
|
|
46
|
+
- Search or filter pages where users expect fresh results
|
|
47
|
+
- Memory-heavy components (maps, large tables, media players)
|
|
48
|
+
- Sensitive flows where data must be cleared on exit
|
|
49
|
+
- Components with heavy background activity you cannot pause
|
|
50
|
+
|
|
51
|
+
## Limit and Control the Cache
|
|
52
|
+
|
|
53
|
+
Always cap cache size with `max` and restrict caching to specific components when possible.
|
|
54
|
+
|
|
55
|
+
```vue
|
|
56
|
+
<template>
|
|
57
|
+
<KeepAlive :max="5" include="Dashboard,Settings">
|
|
58
|
+
<component :is="currentView" />
|
|
59
|
+
</KeepAlive>
|
|
60
|
+
</template>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Ensure Component Names Match include/exclude
|
|
64
|
+
|
|
65
|
+
`include` and `exclude` match the component `name` option. Explicitly set names for reliable caching.
|
|
66
|
+
|
|
67
|
+
```vue
|
|
68
|
+
<!-- TabA.vue -->
|
|
69
|
+
<script setup>
|
|
70
|
+
defineOptions({ name: 'TabA' })
|
|
71
|
+
</script>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```vue
|
|
75
|
+
<template>
|
|
76
|
+
<KeepAlive include="TabA,TabB">
|
|
77
|
+
<component :is="currentTab" />
|
|
78
|
+
</KeepAlive>
|
|
79
|
+
</template>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Cache Invalidation Strategies
|
|
83
|
+
|
|
84
|
+
Vue 3 has no direct API to remove a specific cached instance. Use keys or dynamic include/exclude to force refreshes.
|
|
85
|
+
|
|
86
|
+
```vue
|
|
87
|
+
<script setup>
|
|
88
|
+
import { ref, reactive } from 'vue'
|
|
89
|
+
|
|
90
|
+
const currentView = ref('Dashboard')
|
|
91
|
+
const viewKeys = reactive({ Dashboard: 0, Settings: 0 })
|
|
92
|
+
|
|
93
|
+
function invalidateCache(view) {
|
|
94
|
+
viewKeys[view]++
|
|
95
|
+
}
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<template>
|
|
99
|
+
<KeepAlive>
|
|
100
|
+
<component :is="currentView" :key="`${currentView}-${viewKeys[currentView]}`" />
|
|
101
|
+
</KeepAlive>
|
|
102
|
+
</template>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Lifecycle Hooks for Cached Components
|
|
106
|
+
|
|
107
|
+
Cached components are not destroyed on switch. Use activation hooks for refresh and cleanup.
|
|
108
|
+
|
|
109
|
+
```vue
|
|
110
|
+
<script setup>
|
|
111
|
+
import { onActivated, onDeactivated } from 'vue'
|
|
112
|
+
|
|
113
|
+
onActivated(() => {
|
|
114
|
+
refreshData()
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
onDeactivated(() => {
|
|
118
|
+
pauseTimers()
|
|
119
|
+
})
|
|
120
|
+
</script>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Router Caching and Freshness
|
|
124
|
+
|
|
125
|
+
Decide whether navigation should show cached state or a fresh view. A common pattern is to key by route when params change.
|
|
126
|
+
|
|
127
|
+
```vue
|
|
128
|
+
<template>
|
|
129
|
+
<router-view v-slot="{ Component, route }">
|
|
130
|
+
<KeepAlive>
|
|
131
|
+
<component :is="Component" :key="route.fullPath" />
|
|
132
|
+
</KeepAlive>
|
|
133
|
+
</router-view>
|
|
134
|
+
</template>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
If you want cache reuse but fresh data, refresh in `onActivated` and compare query/params before fetching.
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Component Slots Best Practices
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Poor slot API design causes empty DOM wrappers, weak TypeScript safety, brittle defaults, and unnecessary component overhead
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, slots, components, typescript, composables]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Component Slots Best Practices
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Slots are a core component API surface in Vue. Structure them intentionally so templates stay predictable, typed, and performant.
|
|
12
|
+
|
|
13
|
+
## Task List
|
|
14
|
+
|
|
15
|
+
- Use shorthand syntax for named slots (`#` instead of `v-slot:`)
|
|
16
|
+
- Render optional slot wrapper elements only when slot content exists (`$slots` checks)
|
|
17
|
+
- Type scoped slot contracts with `defineSlots` in TypeScript components
|
|
18
|
+
- Provide fallback content for optional slots
|
|
19
|
+
- Prefer composables over renderless components for pure logic reuse
|
|
20
|
+
|
|
21
|
+
## Shorthand syntax for named slots
|
|
22
|
+
|
|
23
|
+
**BAD:**
|
|
24
|
+
```vue
|
|
25
|
+
<MyComponent>
|
|
26
|
+
<template v-slot:header> ... </template>
|
|
27
|
+
</MyComponent>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**GOOD:**
|
|
31
|
+
```vue
|
|
32
|
+
<MyComponent>
|
|
33
|
+
<template #header> ... </template>
|
|
34
|
+
</MyComponent>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Conditionally Render Optional Slot Wrappers
|
|
38
|
+
|
|
39
|
+
Use `$slots` checks when wrapper elements add spacing, borders, or layout constraints.
|
|
40
|
+
|
|
41
|
+
**BAD:**
|
|
42
|
+
```vue
|
|
43
|
+
<!-- Card.vue -->
|
|
44
|
+
<template>
|
|
45
|
+
<article class="card">
|
|
46
|
+
<header class="card-header">
|
|
47
|
+
<slot name="header" />
|
|
48
|
+
</header>
|
|
49
|
+
|
|
50
|
+
<section class="card-body">
|
|
51
|
+
<slot />
|
|
52
|
+
</section>
|
|
53
|
+
|
|
54
|
+
<footer class="card-footer">
|
|
55
|
+
<slot name="footer" />
|
|
56
|
+
</footer>
|
|
57
|
+
</article>
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**GOOD:**
|
|
62
|
+
```vue
|
|
63
|
+
<!-- Card.vue -->
|
|
64
|
+
<template>
|
|
65
|
+
<article class="card">
|
|
66
|
+
<header v-if="$slots.header" class="card-header">
|
|
67
|
+
<slot name="header" />
|
|
68
|
+
</header>
|
|
69
|
+
|
|
70
|
+
<section v-if="$slots.default" class="card-body">
|
|
71
|
+
<slot />
|
|
72
|
+
</section>
|
|
73
|
+
|
|
74
|
+
<footer v-if="$slots.footer" class="card-footer">
|
|
75
|
+
<slot name="footer" />
|
|
76
|
+
</footer>
|
|
77
|
+
</article>
|
|
78
|
+
</template>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Type Scoped Slot Props with defineSlots
|
|
82
|
+
|
|
83
|
+
In `<script setup lang="ts">`, use `defineSlots` so slot consumers get autocomplete and static checks.
|
|
84
|
+
|
|
85
|
+
**BAD:**
|
|
86
|
+
```vue
|
|
87
|
+
<!-- ProductList.vue -->
|
|
88
|
+
<script setup lang="ts">
|
|
89
|
+
interface Product {
|
|
90
|
+
id: number
|
|
91
|
+
name: string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
defineProps<{ products: Product[] }>()
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<template>
|
|
98
|
+
<ul>
|
|
99
|
+
<li v-for="(product, index) in products" :key="product.id">
|
|
100
|
+
<slot :product="product" :index="index" />
|
|
101
|
+
</li>
|
|
102
|
+
</ul>
|
|
103
|
+
</template>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**GOOD:**
|
|
107
|
+
```vue
|
|
108
|
+
<!-- ProductList.vue -->
|
|
109
|
+
<script setup lang="ts">
|
|
110
|
+
interface Product {
|
|
111
|
+
id: number
|
|
112
|
+
name: string
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
defineProps<{ products: Product[] }>()
|
|
116
|
+
|
|
117
|
+
defineSlots<{
|
|
118
|
+
default(props: { product: Product; index: number }): any
|
|
119
|
+
empty(): any
|
|
120
|
+
}>()
|
|
121
|
+
</script>
|
|
122
|
+
|
|
123
|
+
<template>
|
|
124
|
+
<ul v-if="products.length">
|
|
125
|
+
<li v-for="(product, index) in products" :key="product.id">
|
|
126
|
+
<slot :product="product" :index="index" />
|
|
127
|
+
</li>
|
|
128
|
+
</ul>
|
|
129
|
+
<slot v-else name="empty" />
|
|
130
|
+
</template>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Provide Slot Fallback Content
|
|
134
|
+
|
|
135
|
+
Fallback content makes components resilient when parents omit optional slots.
|
|
136
|
+
|
|
137
|
+
**BAD:**
|
|
138
|
+
```vue
|
|
139
|
+
<!-- SubmitButton.vue -->
|
|
140
|
+
<template>
|
|
141
|
+
<button type="submit" class="btn-primary">
|
|
142
|
+
<slot />
|
|
143
|
+
</button>
|
|
144
|
+
</template>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**GOOD:**
|
|
148
|
+
```vue
|
|
149
|
+
<!-- SubmitButton.vue -->
|
|
150
|
+
<template>
|
|
151
|
+
<button type="submit" class="btn-primary">
|
|
152
|
+
<slot>Submit</slot>
|
|
153
|
+
</button>
|
|
154
|
+
</template>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Prefer Composables for Pure Logic Reuse
|
|
158
|
+
|
|
159
|
+
Renderless components are still useful for slot-driven composition, but composables are usually cleaner for logic-only reuse.
|
|
160
|
+
|
|
161
|
+
**BAD:**
|
|
162
|
+
```vue
|
|
163
|
+
<!-- MouseTracker.vue -->
|
|
164
|
+
<script setup lang="ts">
|
|
165
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
166
|
+
|
|
167
|
+
const x = ref(0)
|
|
168
|
+
const y = ref(0)
|
|
169
|
+
|
|
170
|
+
function onMove(event: MouseEvent) {
|
|
171
|
+
x.value = event.pageX
|
|
172
|
+
y.value = event.pageY
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
onMounted(() => window.addEventListener('mousemove', onMove))
|
|
176
|
+
onUnmounted(() => window.removeEventListener('mousemove', onMove))
|
|
177
|
+
</script>
|
|
178
|
+
|
|
179
|
+
<template>
|
|
180
|
+
<slot :x="x" :y="y" />
|
|
181
|
+
</template>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**GOOD:**
|
|
185
|
+
```ts
|
|
186
|
+
// composables/useMouse.ts
|
|
187
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
188
|
+
|
|
189
|
+
export function useMouse() {
|
|
190
|
+
const x = ref(0)
|
|
191
|
+
const y = ref(0)
|
|
192
|
+
|
|
193
|
+
function onMove(event: MouseEvent) {
|
|
194
|
+
x.value = event.pageX
|
|
195
|
+
y.value = event.pageY
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
onMounted(() => window.addEventListener('mousemove', onMove))
|
|
199
|
+
onUnmounted(() => window.removeEventListener('mousemove', onMove))
|
|
200
|
+
|
|
201
|
+
return { x, y }
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
```vue
|
|
206
|
+
<!-- MousePosition.vue -->
|
|
207
|
+
<script setup lang="ts">
|
|
208
|
+
import { useMouse } from '@/composables/useMouse'
|
|
209
|
+
|
|
210
|
+
const { x, y } = useMouse()
|
|
211
|
+
</script>
|
|
212
|
+
|
|
213
|
+
<template>
|
|
214
|
+
<p>{{ x }}, {{ y }}</p>
|
|
215
|
+
</template>
|
|
216
|
+
```
|