devskill 2.0.2
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 +123 -0
- package/README.vn.md +104 -0
- package/SKILL_GUIDE.vn.md +252 -0
- package/bin/devskill.js +11 -0
- package/meta.ts +123 -0
- package/package.json +21 -0
- package/publish.sh +50 -0
- package/scripts/cli.ts +556 -0
- package/skills/builderx_api-contexts/SKILL.md +63 -0
- package/skills/builderx_api-contexts/references/core-multi-outbox.md +57 -0
- package/skills/builderx_api-controllers/SKILL.md +49 -0
- package/skills/builderx_api-controllers/references/core-fallback.md +62 -0
- package/skills/builderx_api-schemas/SKILL.md +76 -0
- package/skills/builderx_api-schemas/references/core-schema.md +78 -0
- package/skills/builderx_spa-api/SKILL.md +67 -0
- package/skills/builderx_spa-api/references/base-api.md +97 -0
- package/skills/builderx_spa-api/references/fetch.md +70 -0
- package/skills/builderx_spa-design/SKILL.md +82 -0
- package/skills/builderx_spa-design/references/core-text-design.md +64 -0
- package/skills/builderx_spa-design/references/features-feedback-actions.md +76 -0
- package/skills/builderx_spa-design/references/features-forms.md +91 -0
- package/skills/builderx_spa-design/references/features-layout-navigation.md +121 -0
- package/skills/builderx_spa-permission/SKILL.md +38 -0
- package/skills/builderx_spa-permission/references/has-permission.md +34 -0
- package/skills/composition-patterns/AGENTS.md +946 -0
- package/skills/composition-patterns/README.md +60 -0
- package/skills/composition-patterns/SKILL.md +89 -0
- package/skills/composition-patterns/SYNC.md +5 -0
- package/skills/composition-patterns/metadata.json +11 -0
- package/skills/composition-patterns/rules/_sections.md +29 -0
- package/skills/composition-patterns/rules/_template.md +24 -0
- package/skills/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/skills/composition-patterns/rules/architecture-compound-components.md +112 -0
- package/skills/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/skills/composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/skills/composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/skills/composition-patterns/rules/state-context-interface.md +191 -0
- package/skills/composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/skills/composition-patterns/rules/state-lift-state.md +125 -0
- package/skills/deploy-to-vercel/Archive.zip +0 -0
- package/skills/deploy-to-vercel/SKILL.md +296 -0
- package/skills/deploy-to-vercel/SYNC.md +5 -0
- package/skills/deploy-to-vercel/resources/deploy-codex.sh +301 -0
- package/skills/deploy-to-vercel/resources/deploy.sh +301 -0
- package/skills/pinia/SKILL.md +105 -0
- package/skills/pinia/references/api-in-composables.md +139 -0
- package/skills/pinia/references/setup-stores.md +105 -0
- package/skills/pinia/references/using-in-components.md +91 -0
- package/skills/pinia-options/SKILL.md +72 -0
- package/skills/pinia-options/references/core-option-stores.md +149 -0
- package/skills/pinia-options/references/using-in-options-api.md +105 -0
- package/skills/react-best-practices/AGENTS.md +3373 -0
- package/skills/react-best-practices/README.md +123 -0
- package/skills/react-best-practices/SKILL.md +143 -0
- package/skills/react-best-practices/SYNC.md +5 -0
- package/skills/react-best-practices/metadata.json +15 -0
- package/skills/react-best-practices/rules/_sections.md +46 -0
- package/skills/react-best-practices/rules/_template.md +28 -0
- package/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/skills/react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skills/react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/skills/react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/react-best-practices/rules/server-hoist-static-io.md +142 -0
- package/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/skills/react-native-skills/AGENTS.md +2897 -0
- package/skills/react-native-skills/README.md +165 -0
- package/skills/react-native-skills/SKILL.md +121 -0
- package/skills/react-native-skills/SYNC.md +5 -0
- package/skills/react-native-skills/metadata.json +16 -0
- package/skills/react-native-skills/rules/_sections.md +86 -0
- package/skills/react-native-skills/rules/_template.md +28 -0
- package/skills/react-native-skills/rules/animation-derived-value.md +53 -0
- package/skills/react-native-skills/rules/animation-gesture-detector-press.md +95 -0
- package/skills/react-native-skills/rules/animation-gpu-properties.md +65 -0
- package/skills/react-native-skills/rules/design-system-compound-components.md +66 -0
- package/skills/react-native-skills/rules/fonts-config-plugin.md +71 -0
- package/skills/react-native-skills/rules/imports-design-system-folder.md +68 -0
- package/skills/react-native-skills/rules/js-hoist-intl.md +61 -0
- package/skills/react-native-skills/rules/list-performance-callbacks.md +44 -0
- package/skills/react-native-skills/rules/list-performance-function-references.md +132 -0
- package/skills/react-native-skills/rules/list-performance-images.md +53 -0
- package/skills/react-native-skills/rules/list-performance-inline-objects.md +97 -0
- package/skills/react-native-skills/rules/list-performance-item-expensive.md +94 -0
- package/skills/react-native-skills/rules/list-performance-item-memo.md +82 -0
- package/skills/react-native-skills/rules/list-performance-item-types.md +104 -0
- package/skills/react-native-skills/rules/list-performance-virtualize.md +67 -0
- package/skills/react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
- package/skills/react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
- package/skills/react-native-skills/rules/navigation-native-navigators.md +188 -0
- package/skills/react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
- package/skills/react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
- package/skills/react-native-skills/rules/react-state-dispatcher.md +91 -0
- package/skills/react-native-skills/rules/react-state-fallback.md +56 -0
- package/skills/react-native-skills/rules/react-state-minimize.md +65 -0
- package/skills/react-native-skills/rules/rendering-no-falsy-and.md +74 -0
- package/skills/react-native-skills/rules/rendering-text-in-text-component.md +36 -0
- package/skills/react-native-skills/rules/scroll-position-no-state.md +82 -0
- package/skills/react-native-skills/rules/state-ground-truth.md +80 -0
- package/skills/react-native-skills/rules/ui-expo-image.md +66 -0
- package/skills/react-native-skills/rules/ui-image-gallery.md +104 -0
- package/skills/react-native-skills/rules/ui-measure-views.md +78 -0
- package/skills/react-native-skills/rules/ui-menus.md +174 -0
- package/skills/react-native-skills/rules/ui-native-modals.md +77 -0
- package/skills/react-native-skills/rules/ui-pressable.md +61 -0
- package/skills/react-native-skills/rules/ui-safe-area-scroll.md +65 -0
- package/skills/react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
- package/skills/react-native-skills/rules/ui-styling.md +87 -0
- package/skills/slidev/LICENSE.md +21 -0
- package/skills/slidev/README.md +61 -0
- package/skills/slidev/SKILL.md +189 -0
- package/skills/slidev/SYNC.md +5 -0
- package/skills/slidev/references/animation-click-marker.md +37 -0
- package/skills/slidev/references/animation-drawing.md +68 -0
- package/skills/slidev/references/animation-rough-marker.md +53 -0
- package/skills/slidev/references/api-slide-hooks.md +37 -0
- package/skills/slidev/references/build-og-image.md +36 -0
- package/skills/slidev/references/build-pdf.md +40 -0
- package/skills/slidev/references/build-remote-assets.md +34 -0
- package/skills/slidev/references/build-seo-meta.md +43 -0
- package/skills/slidev/references/code-groups.md +64 -0
- package/skills/slidev/references/code-import-snippet.md +55 -0
- package/skills/slidev/references/code-line-highlighting.md +50 -0
- package/skills/slidev/references/code-line-numbers.md +46 -0
- package/skills/slidev/references/code-magic-move.md +57 -0
- package/skills/slidev/references/code-max-height.md +37 -0
- package/skills/slidev/references/code-twoslash.md +42 -0
- package/skills/slidev/references/core-animations.md +196 -0
- package/skills/slidev/references/core-cli.md +140 -0
- package/skills/slidev/references/core-components.md +197 -0
- package/skills/slidev/references/core-exporting.md +148 -0
- package/skills/slidev/references/core-frontmatter.md +195 -0
- package/skills/slidev/references/core-global-context.md +155 -0
- package/skills/slidev/references/core-headmatter.md +188 -0
- package/skills/slidev/references/core-hosting.md +152 -0
- package/skills/slidev/references/core-layouts.md +286 -0
- package/skills/slidev/references/core-syntax.md +155 -0
- package/skills/slidev/references/diagram-latex.md +55 -0
- package/skills/slidev/references/diagram-mermaid.md +44 -0
- package/skills/slidev/references/diagram-plantuml.md +45 -0
- package/skills/slidev/references/editor-monaco-run.md +44 -0
- package/skills/slidev/references/editor-monaco-write.md +24 -0
- package/skills/slidev/references/editor-monaco.md +50 -0
- package/skills/slidev/references/editor-prettier.md +40 -0
- package/skills/slidev/references/editor-side.md +23 -0
- package/skills/slidev/references/editor-vscode.md +55 -0
- package/skills/slidev/references/layout-canvas-size.md +25 -0
- package/skills/slidev/references/layout-draggable.md +57 -0
- package/skills/slidev/references/layout-global-layers.md +50 -0
- package/skills/slidev/references/layout-slots.md +75 -0
- package/skills/slidev/references/layout-transform.md +33 -0
- package/skills/slidev/references/layout-zoom.md +39 -0
- package/skills/slidev/references/presenter-notes-ruby.md +35 -0
- package/skills/slidev/references/presenter-recording.md +30 -0
- package/skills/slidev/references/presenter-remote.md +40 -0
- package/skills/slidev/references/presenter-timer.md +34 -0
- package/skills/slidev/references/style-direction.md +34 -0
- package/skills/slidev/references/style-icons.md +46 -0
- package/skills/slidev/references/style-scoped.md +50 -0
- package/skills/slidev/references/syntax-block-frontmatter.md +39 -0
- package/skills/slidev/references/syntax-frontmatter-merging.md +49 -0
- package/skills/slidev/references/syntax-importing-slides.md +60 -0
- package/skills/slidev/references/syntax-mdc.md +51 -0
- package/skills/slidev/references/tool-eject-theme.md +27 -0
- package/skills/tsdown/LICENSE.md +22 -0
- package/skills/tsdown/README.md +77 -0
- package/skills/tsdown/SKILL.md +407 -0
- package/skills/tsdown/SYNC.md +5 -0
- package/skills/tsdown/references/README.md +139 -0
- package/skills/tsdown/references/advanced-benchmark.md +8 -0
- package/skills/tsdown/references/advanced-ci.md +89 -0
- package/skills/tsdown/references/advanced-hooks.md +363 -0
- package/skills/tsdown/references/advanced-plugins.md +381 -0
- package/skills/tsdown/references/advanced-programmatic.md +378 -0
- package/skills/tsdown/references/advanced-rolldown-options.md +117 -0
- package/skills/tsdown/references/guide-getting-started.md +178 -0
- package/skills/tsdown/references/guide-introduction.md +42 -0
- package/skills/tsdown/references/guide-migrate-from-tsup.md +189 -0
- package/skills/tsdown/references/option-cjs-default.md +98 -0
- package/skills/tsdown/references/option-cleaning.md +275 -0
- package/skills/tsdown/references/option-config-file.md +281 -0
- package/skills/tsdown/references/option-css.md +301 -0
- package/skills/tsdown/references/option-dependencies.md +385 -0
- package/skills/tsdown/references/option-dts.md +251 -0
- package/skills/tsdown/references/option-entry.md +211 -0
- package/skills/tsdown/references/option-exe.md +120 -0
- package/skills/tsdown/references/option-lint.md +127 -0
- package/skills/tsdown/references/option-log-level.md +91 -0
- package/skills/tsdown/references/option-minification.md +177 -0
- package/skills/tsdown/references/option-output-directory.md +270 -0
- package/skills/tsdown/references/option-output-format.md +181 -0
- package/skills/tsdown/references/option-package-exports.md +320 -0
- package/skills/tsdown/references/option-platform.md +256 -0
- package/skills/tsdown/references/option-root.md +88 -0
- package/skills/tsdown/references/option-shims.md +299 -0
- package/skills/tsdown/references/option-sourcemap.md +301 -0
- package/skills/tsdown/references/option-target.md +222 -0
- package/skills/tsdown/references/option-tree-shaking.md +335 -0
- package/skills/tsdown/references/option-unbundle.md +310 -0
- package/skills/tsdown/references/option-watch-mode.md +261 -0
- package/skills/tsdown/references/recipe-react.md +338 -0
- package/skills/tsdown/references/recipe-solid.md +46 -0
- package/skills/tsdown/references/recipe-svelte.md +54 -0
- package/skills/tsdown/references/recipe-vue.md +387 -0
- package/skills/tsdown/references/recipe-wasm.md +125 -0
- package/skills/tsdown/references/reference-cli.md +472 -0
- package/skills/turborepo/LICENSE.md +7 -0
- package/skills/turborepo/SKILL.md +951 -0
- package/skills/turborepo/SYNC.md +5 -0
- package/skills/turborepo/command/turborepo.md +70 -0
- package/skills/turborepo/references/best-practices/RULE.md +241 -0
- package/skills/turborepo/references/best-practices/dependencies.md +246 -0
- package/skills/turborepo/references/best-practices/packages.md +335 -0
- package/skills/turborepo/references/best-practices/structure.md +297 -0
- package/skills/turborepo/references/boundaries/RULE.md +126 -0
- package/skills/turborepo/references/caching/RULE.md +153 -0
- package/skills/turborepo/references/caching/gotchas.md +190 -0
- package/skills/turborepo/references/caching/remote-cache.md +127 -0
- package/skills/turborepo/references/ci/RULE.md +79 -0
- package/skills/turborepo/references/ci/github-actions.md +162 -0
- package/skills/turborepo/references/ci/patterns.md +145 -0
- package/skills/turborepo/references/ci/vercel.md +103 -0
- package/skills/turborepo/references/cli/RULE.md +100 -0
- package/skills/turborepo/references/cli/commands.md +297 -0
- package/skills/turborepo/references/configuration/RULE.md +235 -0
- package/skills/turborepo/references/configuration/global-options.md +239 -0
- package/skills/turborepo/references/configuration/gotchas.md +368 -0
- package/skills/turborepo/references/configuration/tasks.md +325 -0
- package/skills/turborepo/references/environment/RULE.md +123 -0
- package/skills/turborepo/references/environment/gotchas.md +175 -0
- package/skills/turborepo/references/environment/modes.md +101 -0
- package/skills/turborepo/references/filtering/RULE.md +148 -0
- package/skills/turborepo/references/filtering/patterns.md +152 -0
- package/skills/turborepo/references/watch/RULE.md +99 -0
- package/skills/vercel-cli-with-tokens/SKILL.md +328 -0
- package/skills/vercel-cli-with-tokens/SYNC.md +5 -0
- package/skills/vue/SKILL.md +90 -0
- package/skills/vue/references/composables.md +54 -0
- package/skills/vue/references/lifecycle.md +31 -0
- package/skills/vue/references/props-emits.md +62 -0
- package/skills/vue/references/provide-inject.md +47 -0
- package/skills/vue/references/state.md +65 -0
- package/skills/vue-antdv-tailwind/SKILL.md +33 -0
- package/skills/vue-antdv-tailwind/references/styling-rules.md +61 -0
- package/skills/vue-best-practices/LICENSE.md +21 -0
- package/skills/vue-best-practices/SKILL.md +154 -0
- package/skills/vue-best-practices/SYNC.md +5 -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 +307 -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 +344 -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-jsx-best-practices/LICENSE.md +21 -0
- package/skills/vue-jsx-best-practices/SKILL.md +12 -0
- package/skills/vue-jsx-best-practices/SYNC.md +5 -0
- package/skills/vue-jsx-best-practices/reference/render-function-jsx-vue-vs-react.md +141 -0
- package/skills/vue-options/SKILL.md +98 -0
- package/skills/vue-options/references/lifecycle.md +107 -0
- package/skills/vue-options/references/mixins.md +96 -0
- package/skills/vue-options/references/props-emits.md +106 -0
- package/skills/vue-options/references/provide-inject.md +92 -0
- package/skills/vue-options/references/state.md +115 -0
- package/skills/vue-options-api-best-practices/LICENSE.md +21 -0
- package/skills/vue-options-api-best-practices/SKILL.md +23 -0
- package/skills/vue-options-api-best-practices/SYNC.md +5 -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/LICENSE.md +21 -0
- package/skills/vue-pinia-best-practices/SKILL.md +21 -0
- package/skills/vue-pinia-best-practices/SYNC.md +5 -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/LICENSE.md +21 -0
- package/skills/vue-router-best-practices/SKILL.md +23 -0
- package/skills/vue-router-best-practices/SYNC.md +5 -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/LICENSE.md +21 -0
- package/skills/vue-testing-best-practices/SKILL.md +29 -0
- package/skills/vue-testing-best-practices/SYNC.md +5 -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
- package/skills/vueuse-functions/LICENSE.md +21 -0
- package/skills/vueuse-functions/SKILL.md +419 -0
- package/skills/vueuse-functions/SYNC.md +5 -0
- package/skills/vueuse-functions/references/computedAsync.md +195 -0
- package/skills/vueuse-functions/references/computedEager.md +62 -0
- package/skills/vueuse-functions/references/computedInject.md +137 -0
- package/skills/vueuse-functions/references/computedWithControl.md +98 -0
- package/skills/vueuse-functions/references/createEventHook.md +86 -0
- package/skills/vueuse-functions/references/createGenericProjection.md +25 -0
- package/skills/vueuse-functions/references/createGlobalState.md +95 -0
- package/skills/vueuse-functions/references/createInjectionState.md +215 -0
- package/skills/vueuse-functions/references/createProjection.md +31 -0
- package/skills/vueuse-functions/references/createRef.md +54 -0
- package/skills/vueuse-functions/references/createReusableTemplate.md +357 -0
- package/skills/vueuse-functions/references/createSharedComposable.md +42 -0
- package/skills/vueuse-functions/references/createTemplatePromise.md +306 -0
- package/skills/vueuse-functions/references/createUnrefFn.md +51 -0
- package/skills/vueuse-functions/references/extendRef.md +76 -0
- package/skills/vueuse-functions/references/from.md +80 -0
- package/skills/vueuse-functions/references/get.md +30 -0
- package/skills/vueuse-functions/references/injectLocal.md +35 -0
- package/skills/vueuse-functions/references/isDefined.md +31 -0
- package/skills/vueuse-functions/references/logicAnd.md +40 -0
- package/skills/vueuse-functions/references/logicNot.md +36 -0
- package/skills/vueuse-functions/references/logicOr.md +40 -0
- package/skills/vueuse-functions/references/makeDestructurable.md +41 -0
- package/skills/vueuse-functions/references/onClickOutside.md +221 -0
- package/skills/vueuse-functions/references/onElementRemoval.md +88 -0
- package/skills/vueuse-functions/references/onKeyStroke.md +211 -0
- package/skills/vueuse-functions/references/onLongPress.md +227 -0
- package/skills/vueuse-functions/references/onStartTyping.md +53 -0
- package/skills/vueuse-functions/references/provideLocal.md +37 -0
- package/skills/vueuse-functions/references/reactify.md +144 -0
- package/skills/vueuse-functions/references/reactifyObject.md +62 -0
- package/skills/vueuse-functions/references/reactiveComputed.md +34 -0
- package/skills/vueuse-functions/references/reactiveOmit.md +86 -0
- package/skills/vueuse-functions/references/reactivePick.md +106 -0
- package/skills/vueuse-functions/references/refAutoReset.md +46 -0
- package/skills/vueuse-functions/references/refDebounced.md +81 -0
- package/skills/vueuse-functions/references/refDefault.md +36 -0
- package/skills/vueuse-functions/references/refManualReset.md +44 -0
- package/skills/vueuse-functions/references/refThrottled.md +99 -0
- package/skills/vueuse-functions/references/refWithControl.md +146 -0
- package/skills/vueuse-functions/references/set.md +30 -0
- package/skills/vueuse-functions/references/syncRef.md +195 -0
- package/skills/vueuse-functions/references/syncRefs.md +128 -0
- package/skills/vueuse-functions/references/templateRef.md +86 -0
- package/skills/vueuse-functions/references/toObserver.md +38 -0
- package/skills/vueuse-functions/references/toReactive.md +41 -0
- package/skills/vueuse-functions/references/toRef.md +75 -0
- package/skills/vueuse-functions/references/toRefs.md +81 -0
- package/skills/vueuse-functions/references/tryOnBeforeMount.md +34 -0
- package/skills/vueuse-functions/references/tryOnBeforeUnmount.md +32 -0
- package/skills/vueuse-functions/references/tryOnMounted.md +34 -0
- package/skills/vueuse-functions/references/tryOnScopeDispose.md +31 -0
- package/skills/vueuse-functions/references/tryOnUnmounted.md +32 -0
- package/skills/vueuse-functions/references/unrefElement.md +54 -0
- package/skills/vueuse-functions/references/until.md +161 -0
- package/skills/vueuse-functions/references/useAbs.md +31 -0
- package/skills/vueuse-functions/references/useActiveElement.md +85 -0
- package/skills/vueuse-functions/references/useAnimate.md +181 -0
- package/skills/vueuse-functions/references/useArrayDifference.md +84 -0
- package/skills/vueuse-functions/references/useArrayEvery.md +59 -0
- package/skills/vueuse-functions/references/useArrayFilter.md +63 -0
- package/skills/vueuse-functions/references/useArrayFind.md +50 -0
- package/skills/vueuse-functions/references/useArrayFindIndex.md +59 -0
- package/skills/vueuse-functions/references/useArrayFindLast.md +52 -0
- package/skills/vueuse-functions/references/useArrayIncludes.md +63 -0
- package/skills/vueuse-functions/references/useArrayJoin.md +74 -0
- package/skills/vueuse-functions/references/useArrayMap.md +59 -0
- package/skills/vueuse-functions/references/useArrayReduce.md +81 -0
- package/skills/vueuse-functions/references/useArraySome.md +59 -0
- package/skills/vueuse-functions/references/useArrayUnique.md +76 -0
- package/skills/vueuse-functions/references/useAsyncQueue.md +136 -0
- package/skills/vueuse-functions/references/useAsyncState.md +185 -0
- package/skills/vueuse-functions/references/useAsyncValidator.md +70 -0
- package/skills/vueuse-functions/references/useAuth.md +123 -0
- package/skills/vueuse-functions/references/useAverage.md +36 -0
- package/skills/vueuse-functions/references/useAxios.md +325 -0
- package/skills/vueuse-functions/references/useBase64.md +136 -0
- package/skills/vueuse-functions/references/useBattery.md +78 -0
- package/skills/vueuse-functions/references/useBluetooth.md +175 -0
- package/skills/vueuse-functions/references/useBreakpoints.md +172 -0
- package/skills/vueuse-functions/references/useBroadcastChannel.md +74 -0
- package/skills/vueuse-functions/references/useBrowserLocation.md +83 -0
- package/skills/vueuse-functions/references/useCached.md +52 -0
- package/skills/vueuse-functions/references/useCeil.md +31 -0
- package/skills/vueuse-functions/references/useChangeCase.md +79 -0
- package/skills/vueuse-functions/references/useClamp.md +85 -0
- package/skills/vueuse-functions/references/useClipboard.md +123 -0
- package/skills/vueuse-functions/references/useClipboardItems.md +94 -0
- package/skills/vueuse-functions/references/useCloned.md +91 -0
- package/skills/vueuse-functions/references/useColorMode.md +172 -0
- package/skills/vueuse-functions/references/useConfirmDialog.md +159 -0
- package/skills/vueuse-functions/references/useCookies.md +162 -0
- package/skills/vueuse-functions/references/useCountdown.md +105 -0
- package/skills/vueuse-functions/references/useCounter.md +86 -0
- package/skills/vueuse-functions/references/useCssSupports.md +35 -0
- package/skills/vueuse-functions/references/useCssVar.md +50 -0
- package/skills/vueuse-functions/references/useCurrentElement.md +61 -0
- package/skills/vueuse-functions/references/useCycleList.md +75 -0
- package/skills/vueuse-functions/references/useDark.md +144 -0
- package/skills/vueuse-functions/references/useDateFormat.md +145 -0
- package/skills/vueuse-functions/references/useDebounceFn.md +100 -0
- package/skills/vueuse-functions/references/useDebouncedRefHistory.md +40 -0
- package/skills/vueuse-functions/references/useDeviceMotion.md +86 -0
- package/skills/vueuse-functions/references/useDeviceOrientation.md +62 -0
- package/skills/vueuse-functions/references/useDevicePixelRatio.md +44 -0
- package/skills/vueuse-functions/references/useDevicesList.md +90 -0
- package/skills/vueuse-functions/references/useDisplayMedia.md +71 -0
- package/skills/vueuse-functions/references/useDocumentVisibility.md +45 -0
- package/skills/vueuse-functions/references/useDraggable.md +317 -0
- package/skills/vueuse-functions/references/useDrauu.md +65 -0
- package/skills/vueuse-functions/references/useDropZone.md +83 -0
- package/skills/vueuse-functions/references/useElementBounding.md +131 -0
- package/skills/vueuse-functions/references/useElementByPoint.md +48 -0
- package/skills/vueuse-functions/references/useElementHover.md +79 -0
- package/skills/vueuse-functions/references/useElementSize.md +78 -0
- package/skills/vueuse-functions/references/useElementVisibility.md +123 -0
- package/skills/vueuse-functions/references/useEventBus.md +101 -0
- package/skills/vueuse-functions/references/useEventListener.md +226 -0
- package/skills/vueuse-functions/references/useEventSource.md +204 -0
- package/skills/vueuse-functions/references/useExtractedObservable.md +198 -0
- package/skills/vueuse-functions/references/useEyeDropper.md +71 -0
- package/skills/vueuse-functions/references/useFavicon.md +67 -0
- package/skills/vueuse-functions/references/useFetch.md +546 -0
- package/skills/vueuse-functions/references/useFileDialog.md +91 -0
- package/skills/vueuse-functions/references/useFileSystemAccess.md +162 -0
- package/skills/vueuse-functions/references/useFirestore.md +129 -0
- package/skills/vueuse-functions/references/useFloor.md +31 -0
- package/skills/vueuse-functions/references/useFocus.md +99 -0
- package/skills/vueuse-functions/references/useFocusTrap.md +245 -0
- package/skills/vueuse-functions/references/useFocusWithin.md +57 -0
- package/skills/vueuse-functions/references/useFps.md +28 -0
- package/skills/vueuse-functions/references/useFullscreen.md +75 -0
- package/skills/vueuse-functions/references/useFuse.md +107 -0
- package/skills/vueuse-functions/references/useGamepad.md +222 -0
- package/skills/vueuse-functions/references/useGeolocation.md +85 -0
- package/skills/vueuse-functions/references/useIDBKeyval.md +93 -0
- package/skills/vueuse-functions/references/useIdle.md +88 -0
- package/skills/vueuse-functions/references/useImage.md +86 -0
- package/skills/vueuse-functions/references/useInfiniteScroll.md +157 -0
- package/skills/vueuse-functions/references/useIntersectionObserver.md +118 -0
- package/skills/vueuse-functions/references/useInterval.md +112 -0
- package/skills/vueuse-functions/references/useIntervalFn.md +50 -0
- package/skills/vueuse-functions/references/useIpcRenderer.md +144 -0
- package/skills/vueuse-functions/references/useIpcRendererInvoke.md +58 -0
- package/skills/vueuse-functions/references/useIpcRendererOn.md +52 -0
- package/skills/vueuse-functions/references/useJwt.md +57 -0
- package/skills/vueuse-functions/references/useKeyModifier.md +87 -0
- package/skills/vueuse-functions/references/useLastChanged.md +63 -0
- package/skills/vueuse-functions/references/useLocalStorage.md +41 -0
- package/skills/vueuse-functions/references/useMagicKeys.md +245 -0
- package/skills/vueuse-functions/references/useManualRefHistory.md +204 -0
- package/skills/vueuse-functions/references/useMath.md +47 -0
- package/skills/vueuse-functions/references/useMax.md +36 -0
- package/skills/vueuse-functions/references/useMediaControls.md +571 -0
- package/skills/vueuse-functions/references/useMediaQuery.md +53 -0
- package/skills/vueuse-functions/references/useMemoize.md +175 -0
- package/skills/vueuse-functions/references/useMemory.md +71 -0
- package/skills/vueuse-functions/references/useMin.md +36 -0
- package/skills/vueuse-functions/references/useMounted.md +38 -0
- package/skills/vueuse-functions/references/useMouse.md +113 -0
- package/skills/vueuse-functions/references/useMouseInElement.md +123 -0
- package/skills/vueuse-functions/references/useMousePressed.md +112 -0
- package/skills/vueuse-functions/references/useMutationObserver.md +61 -0
- package/skills/vueuse-functions/references/useNProgress.md +78 -0
- package/skills/vueuse-functions/references/useNavigatorLanguage.md +57 -0
- package/skills/vueuse-functions/references/useNetwork.md +106 -0
- package/skills/vueuse-functions/references/useNow.md +79 -0
- package/skills/vueuse-functions/references/useObjectUrl.md +55 -0
- package/skills/vueuse-functions/references/useObservable.md +91 -0
- package/skills/vueuse-functions/references/useOffsetPagination.md +199 -0
- package/skills/vueuse-functions/references/useOnline.md +41 -0
- package/skills/vueuse-functions/references/usePageLeave.md +42 -0
- package/skills/vueuse-functions/references/useParallax.md +58 -0
- package/skills/vueuse-functions/references/useParentElement.md +54 -0
- package/skills/vueuse-functions/references/usePerformanceObserver.md +48 -0
- package/skills/vueuse-functions/references/usePermission.md +79 -0
- package/skills/vueuse-functions/references/usePointer.md +89 -0
- package/skills/vueuse-functions/references/usePointerLock.md +60 -0
- package/skills/vueuse-functions/references/usePointerSwipe.md +80 -0
- package/skills/vueuse-functions/references/usePrecision.md +49 -0
- package/skills/vueuse-functions/references/usePreferredColorScheme.md +42 -0
- package/skills/vueuse-functions/references/usePreferredContrast.md +42 -0
- package/skills/vueuse-functions/references/usePreferredDark.md +41 -0
- package/skills/vueuse-functions/references/usePreferredLanguages.md +41 -0
- package/skills/vueuse-functions/references/usePreferredReducedMotion.md +42 -0
- package/skills/vueuse-functions/references/usePreferredReducedTransparency.md +42 -0
- package/skills/vueuse-functions/references/usePrevious.md +40 -0
- package/skills/vueuse-functions/references/useProjection.md +38 -0
- package/skills/vueuse-functions/references/useQRCode.md +53 -0
- package/skills/vueuse-functions/references/useRTDB.md +83 -0
- package/skills/vueuse-functions/references/useRafFn.md +68 -0
- package/skills/vueuse-functions/references/useRefHistory.md +285 -0
- package/skills/vueuse-functions/references/useResizeObserver.md +109 -0
- package/skills/vueuse-functions/references/useRound.md +31 -0
- package/skills/vueuse-functions/references/useRouteHash.md +27 -0
- package/skills/vueuse-functions/references/useRouteParams.md +38 -0
- package/skills/vueuse-functions/references/useRouteQuery.md +79 -0
- package/skills/vueuse-functions/references/useSSRWidth.md +47 -0
- package/skills/vueuse-functions/references/useScreenOrientation.md +96 -0
- package/skills/vueuse-functions/references/useScreenSafeArea.md +60 -0
- package/skills/vueuse-functions/references/useScriptTag.md +116 -0
- package/skills/vueuse-functions/references/useScroll.md +238 -0
- package/skills/vueuse-functions/references/useScrollLock.md +66 -0
- package/skills/vueuse-functions/references/useSessionStorage.md +41 -0
- package/skills/vueuse-functions/references/useShare.md +68 -0
- package/skills/vueuse-functions/references/useSortable.md +235 -0
- package/skills/vueuse-functions/references/useSorted.md +90 -0
- package/skills/vueuse-functions/references/useSpeechRecognition.md +94 -0
- package/skills/vueuse-functions/references/useSpeechSynthesis.md +105 -0
- package/skills/vueuse-functions/references/useStepper.md +137 -0
- package/skills/vueuse-functions/references/useStorage.md +278 -0
- package/skills/vueuse-functions/references/useStorageAsync.md +136 -0
- package/skills/vueuse-functions/references/useStyleTag.md +131 -0
- package/skills/vueuse-functions/references/useSubject.md +77 -0
- package/skills/vueuse-functions/references/useSubscription.md +33 -0
- package/skills/vueuse-functions/references/useSum.md +36 -0
- package/skills/vueuse-functions/references/useSupported.md +29 -0
- package/skills/vueuse-functions/references/useSwipe.md +75 -0
- package/skills/vueuse-functions/references/useTemplateRefsList.md +37 -0
- package/skills/vueuse-functions/references/useTextDirection.md +75 -0
- package/skills/vueuse-functions/references/useTextSelection.md +40 -0
- package/skills/vueuse-functions/references/useTextareaAutosize.md +97 -0
- package/skills/vueuse-functions/references/useThrottleFn.md +57 -0
- package/skills/vueuse-functions/references/useThrottledRefHistory.md +47 -0
- package/skills/vueuse-functions/references/useTimeAgo.md +154 -0
- package/skills/vueuse-functions/references/useTimeAgoIntl.md +117 -0
- package/skills/vueuse-functions/references/useTimeout.md +113 -0
- package/skills/vueuse-functions/references/useTimeoutFn.md +51 -0
- package/skills/vueuse-functions/references/useTimeoutPoll.md +47 -0
- package/skills/vueuse-functions/references/useTimestamp.md +89 -0
- package/skills/vueuse-functions/references/useTitle.md +113 -0
- package/skills/vueuse-functions/references/useToNumber.md +54 -0
- package/skills/vueuse-functions/references/useToString.md +34 -0
- package/skills/vueuse-functions/references/useToggle.md +103 -0
- package/skills/vueuse-functions/references/useTransition.md +265 -0
- package/skills/vueuse-functions/references/useTrunc.md +33 -0
- package/skills/vueuse-functions/references/useUrlSearchParams.md +121 -0
- package/skills/vueuse-functions/references/useUserMedia.md +1138 -0
- package/skills/vueuse-functions/references/useVModel.md +182 -0
- package/skills/vueuse-functions/references/useVModels.md +67 -0
- package/skills/vueuse-functions/references/useVibrate.md +87 -0
- package/skills/vueuse-functions/references/useVirtualList.md +182 -0
- package/skills/vueuse-functions/references/useWakeLock.md +50 -0
- package/skills/vueuse-functions/references/useWebNotification.md +176 -0
- package/skills/vueuse-functions/references/useWebSocket.md +299 -0
- package/skills/vueuse-functions/references/useWebWorker.md +60 -0
- package/skills/vueuse-functions/references/useWebWorkerFn.md +102 -0
- package/skills/vueuse-functions/references/useWindowFocus.md +46 -0
- package/skills/vueuse-functions/references/useWindowScroll.md +61 -0
- package/skills/vueuse-functions/references/useWindowSize.md +76 -0
- package/skills/vueuse-functions/references/useZoomFactor.md +53 -0
- package/skills/vueuse-functions/references/useZoomLevel.md +53 -0
- package/skills/vueuse-functions/references/watchArray.md +53 -0
- package/skills/vueuse-functions/references/watchAtMost.md +55 -0
- package/skills/vueuse-functions/references/watchDebounced.md +101 -0
- package/skills/vueuse-functions/references/watchDeep.md +54 -0
- package/skills/vueuse-functions/references/watchExtractedObservable.md +192 -0
- package/skills/vueuse-functions/references/watchIgnorable.md +120 -0
- package/skills/vueuse-functions/references/watchImmediate.md +44 -0
- package/skills/vueuse-functions/references/watchOnce.md +41 -0
- package/skills/vueuse-functions/references/watchPausable.md +86 -0
- package/skills/vueuse-functions/references/watchThrottled.md +108 -0
- package/skills/vueuse-functions/references/watchTriggerable.md +98 -0
- package/skills/vueuse-functions/references/watchWithFilter.md +54 -0
- package/skills/vueuse-functions/references/whenever.md +100 -0
- package/skills/web-design-guidelines/SKILL.md +39 -0
- package/skills/web-design-guidelines/SYNC.md +5 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Avoid Excessive Component Abstraction in Large Lists
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Each component instance has memory and render overhead - abstractions multiply this in lists
|
|
5
|
+
type: efficiency
|
|
6
|
+
tags: [vue3, performance, components, abstraction, lists, optimization]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Avoid Excessive Component Abstraction in Large Lists
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Component instances are more expensive than plain DOM nodes. While abstractions improve code organization, unnecessary nesting creates overhead. In large lists, this overhead multiplies - 100 items with 3 levels of abstraction means 300+ component instances instead of 100.
|
|
12
|
+
|
|
13
|
+
Don't avoid abstraction entirely, but be mindful of component depth in frequently-rendered elements like list items.
|
|
14
|
+
|
|
15
|
+
## Task List
|
|
16
|
+
|
|
17
|
+
- Review list item components for unnecessary wrapper components
|
|
18
|
+
- Consider flattening component hierarchies in hot paths
|
|
19
|
+
- Use native elements when a component adds no value
|
|
20
|
+
- Profile component counts using Vue DevTools
|
|
21
|
+
- Focus optimization efforts on the most-rendered components
|
|
22
|
+
|
|
23
|
+
**BAD:**
|
|
24
|
+
```vue
|
|
25
|
+
<!-- BAD: Deep abstraction in list items -->
|
|
26
|
+
<template>
|
|
27
|
+
<div class="user-list">
|
|
28
|
+
<!-- For 100 users: Creates 400 component instances -->
|
|
29
|
+
<UserCard v-for="user in users" :key="user.id" :user="user" />
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<!-- UserCard.vue -->
|
|
34
|
+
<template>
|
|
35
|
+
<Card> <!-- Wrapper component #1 -->
|
|
36
|
+
<CardHeader> <!-- Wrapper component #2 -->
|
|
37
|
+
<UserAvatar :src="user.avatar" /> <!-- Wrapper component #3 -->
|
|
38
|
+
</CardHeader>
|
|
39
|
+
<CardBody> <!-- Wrapper component #4 -->
|
|
40
|
+
<Text>{{ user.name }}</Text>
|
|
41
|
+
</CardBody>
|
|
42
|
+
</Card>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<!-- Each UserCard creates: Card + CardHeader + CardBody + UserAvatar + Text
|
|
46
|
+
100 users = 500+ component instances -->
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**GOOD:**
|
|
50
|
+
```vue
|
|
51
|
+
<!-- GOOD: Flattened structure in list items -->
|
|
52
|
+
<template>
|
|
53
|
+
<div class="user-list">
|
|
54
|
+
<!-- For 100 users: Creates 100 component instances -->
|
|
55
|
+
<UserCard v-for="user in users" :key="user.id" :user="user" />
|
|
56
|
+
</div>
|
|
57
|
+
</template>
|
|
58
|
+
|
|
59
|
+
<!-- UserCard.vue - Flattened, uses native elements -->
|
|
60
|
+
<template>
|
|
61
|
+
<div class="card">
|
|
62
|
+
<div class="card-header">
|
|
63
|
+
<img :src="user.avatar" :alt="user.name" class="avatar" />
|
|
64
|
+
</div>
|
|
65
|
+
<div class="card-body">
|
|
66
|
+
<span class="user-name">{{ user.name }}</span>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<script setup>
|
|
72
|
+
defineProps({
|
|
73
|
+
user: Object
|
|
74
|
+
})
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<style scoped>
|
|
78
|
+
/* Styles that would have been in Card, CardHeader, etc. */
|
|
79
|
+
.card { /* ... */ }
|
|
80
|
+
.card-header { /* ... */ }
|
|
81
|
+
.card-body { /* ... */ }
|
|
82
|
+
.avatar { /* ... */ }
|
|
83
|
+
</style>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## When Abstraction Is Still Worth It
|
|
87
|
+
|
|
88
|
+
```vue
|
|
89
|
+
<!-- Component abstraction is valuable when: -->
|
|
90
|
+
|
|
91
|
+
<!-- 1. Complex behavior is encapsulated -->
|
|
92
|
+
<UserStatusIndicator :user="user" /> <!-- Has logic, tooltips, etc. -->
|
|
93
|
+
|
|
94
|
+
<!-- 2. Reused outside of the hot path -->
|
|
95
|
+
<Card> <!-- OK to use in one-off places, not in 100-item lists -->
|
|
96
|
+
|
|
97
|
+
<!-- 3. The list itself is small -->
|
|
98
|
+
<template v-if="items.length < 20">
|
|
99
|
+
<FancyItem v-for="item in items" :key="item.id" />
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
<!-- 4. Virtualization is used (only ~20 items rendered at once) -->
|
|
103
|
+
<RecycleScroller :items="items">
|
|
104
|
+
<template #default="{ item }">
|
|
105
|
+
<ComplexItem :item="item" /> <!-- OK - only 20 instances exist -->
|
|
106
|
+
</template>
|
|
107
|
+
</RecycleScroller>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Measuring Component Overhead
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
// In development, profile component counts
|
|
114
|
+
import { onMounted, getCurrentInstance } from 'vue'
|
|
115
|
+
|
|
116
|
+
onMounted(() => {
|
|
117
|
+
const instance = getCurrentInstance()
|
|
118
|
+
let count = 0
|
|
119
|
+
|
|
120
|
+
function countComponents(vnode) {
|
|
121
|
+
if (vnode.component) count++
|
|
122
|
+
if (vnode.children) {
|
|
123
|
+
vnode.children.forEach(child => {
|
|
124
|
+
if (child.component || child.children) countComponents(child)
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Use Vue DevTools instead for accurate counts
|
|
130
|
+
console.log('Check Vue DevTools Components tab for instance counts')
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Alternatives to Wrapper Components
|
|
135
|
+
|
|
136
|
+
```vue
|
|
137
|
+
<!-- Instead of a <Button> component for styling: -->
|
|
138
|
+
<button class="btn btn-primary">Click</button>
|
|
139
|
+
|
|
140
|
+
<!-- Instead of a <Text> component: -->
|
|
141
|
+
<span class="text-body">{{ content }}</span>
|
|
142
|
+
|
|
143
|
+
<!-- Instead of layout wrapper components in lists: -->
|
|
144
|
+
<div class="flex items-center gap-2">
|
|
145
|
+
<!-- content -->
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<!-- Use CSS classes or Tailwind instead of component abstractions for styling -->
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Impact Calculation
|
|
152
|
+
|
|
153
|
+
| List Size | Components per Item | Total Instances | Memory Impact |
|
|
154
|
+
|-----------|---------------------|-----------------|---------------|
|
|
155
|
+
| 100 items | 1 (flat) | 100 | Baseline |
|
|
156
|
+
| 100 items | 3 (nested) | 300 | ~3x memory |
|
|
157
|
+
| 100 items | 5 (deeply nested) | 500 | ~5x memory |
|
|
158
|
+
| 1000 items | 1 (flat) | 1000 | High |
|
|
159
|
+
| 1000 items | 5 (deeply nested) | 5000 | Very High |
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use v-once and v-memo to Skip Unnecessary Updates
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: v-once skips all future updates for static content; v-memo conditionally memoizes subtrees
|
|
5
|
+
type: efficiency
|
|
6
|
+
tags: [vue3, performance, v-once, v-memo, optimization, directives]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Use v-once and v-memo to Skip Unnecessary Updates
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Vue re-evaluates templates on every reactive change. For content that never changes or changes infrequently, `v-once` and `v-memo` tell Vue to skip updates, reducing render work.
|
|
12
|
+
|
|
13
|
+
Use `v-once` for truly static content and `v-memo` for conditionally-static content in lists.
|
|
14
|
+
|
|
15
|
+
## Task List
|
|
16
|
+
|
|
17
|
+
- Apply `v-once` to elements that use runtime data but never need updating
|
|
18
|
+
- Apply `v-memo` to list items that should only update on specific condition changes
|
|
19
|
+
- Verify memoized content doesn't need to respond to other state changes
|
|
20
|
+
- Profile with Vue DevTools to confirm update skipping
|
|
21
|
+
|
|
22
|
+
## v-once: Render Once, Never Update
|
|
23
|
+
|
|
24
|
+
**BAD:**
|
|
25
|
+
```vue
|
|
26
|
+
<template>
|
|
27
|
+
<!-- BAD: Re-evaluated on every parent re-render -->
|
|
28
|
+
<div class="terms-content">
|
|
29
|
+
<h1>Terms of Service</h1>
|
|
30
|
+
<p>Version: {{ termsVersion }}</p>
|
|
31
|
+
<div v-html="termsContent"></div>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<!-- This content NEVER changes, but Vue checks it every render -->
|
|
35
|
+
<footer>
|
|
36
|
+
<p>Copyright {{ copyrightYear }} {{ companyName }}</p>
|
|
37
|
+
</footer>
|
|
38
|
+
</template>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**GOOD:**
|
|
42
|
+
```vue
|
|
43
|
+
<template>
|
|
44
|
+
<!-- GOOD: Rendered once, skipped on all future updates -->
|
|
45
|
+
<div class="terms-content" v-once>
|
|
46
|
+
<h1>Terms of Service</h1>
|
|
47
|
+
<p>Version: {{ termsVersion }}</p>
|
|
48
|
+
<div v-html="termsContent"></div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- v-once tells Vue this never needs to update -->
|
|
52
|
+
<footer v-once>
|
|
53
|
+
<p>Copyright {{ copyrightYear }} {{ companyName }}</p>
|
|
54
|
+
</footer>
|
|
55
|
+
</template>
|
|
56
|
+
|
|
57
|
+
<script setup>
|
|
58
|
+
// These values are set once at component creation
|
|
59
|
+
const termsVersion = '2.1'
|
|
60
|
+
const termsContent = fetchedTermsHTML
|
|
61
|
+
const copyrightYear = 2024
|
|
62
|
+
const companyName = 'Acme Corp'
|
|
63
|
+
</script>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## v-memo: Conditional Memoization for Lists
|
|
67
|
+
|
|
68
|
+
**BAD:**
|
|
69
|
+
```vue
|
|
70
|
+
<template>
|
|
71
|
+
<!-- BAD: All items re-render when selectedId changes -->
|
|
72
|
+
<div v-for="item in list" :key="item.id">
|
|
73
|
+
<div :class="{ selected: item.id === selectedId }">
|
|
74
|
+
<ExpensiveComponent :data="item" />
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**GOOD:**
|
|
81
|
+
```vue
|
|
82
|
+
<template>
|
|
83
|
+
<!-- GOOD: Items only re-render when their selection state changes -->
|
|
84
|
+
<div
|
|
85
|
+
v-for="item in list"
|
|
86
|
+
:key="item.id"
|
|
87
|
+
v-memo="[item.id === selectedId]"
|
|
88
|
+
>
|
|
89
|
+
<div :class="{ selected: item.id === selectedId }">
|
|
90
|
+
<ExpensiveComponent :data="item" />
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</template>
|
|
94
|
+
|
|
95
|
+
<script setup>
|
|
96
|
+
import { ref } from 'vue'
|
|
97
|
+
|
|
98
|
+
const list = ref([/* many items */])
|
|
99
|
+
const selectedId = ref(null)
|
|
100
|
+
|
|
101
|
+
// When selectedId changes:
|
|
102
|
+
// - Only the previously-selected item re-renders (selected: true -> false)
|
|
103
|
+
// - Only the newly-selected item re-renders (selected: false -> true)
|
|
104
|
+
// - All other items are SKIPPED (v-memo values unchanged)
|
|
105
|
+
</script>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## v-memo with Multiple Dependencies
|
|
109
|
+
|
|
110
|
+
```vue
|
|
111
|
+
<template>
|
|
112
|
+
<!-- Re-render only when item's selection OR editing state changes -->
|
|
113
|
+
<div
|
|
114
|
+
v-for="item in items"
|
|
115
|
+
:key="item.id"
|
|
116
|
+
v-memo="[item.id === selectedId, item.id === editingId]"
|
|
117
|
+
>
|
|
118
|
+
<ItemCard
|
|
119
|
+
:item="item"
|
|
120
|
+
:selected="item.id === selectedId"
|
|
121
|
+
:editing="item.id === editingId"
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
</template>
|
|
125
|
+
|
|
126
|
+
<script setup>
|
|
127
|
+
const selectedId = ref(null)
|
|
128
|
+
const editingId = ref(null)
|
|
129
|
+
const items = ref([/* ... */])
|
|
130
|
+
</script>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## v-memo with Empty Array = v-once
|
|
134
|
+
|
|
135
|
+
```vue
|
|
136
|
+
<template>
|
|
137
|
+
<!-- v-memo="[]" is equivalent to v-once -->
|
|
138
|
+
<div v-for="item in staticList" :key="item.id" v-memo="[]">
|
|
139
|
+
{{ item.name }}
|
|
140
|
+
</div>
|
|
141
|
+
</template>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## When NOT to Use These Directives
|
|
145
|
+
|
|
146
|
+
```vue
|
|
147
|
+
<template>
|
|
148
|
+
<!-- DON'T: Content that DOES need to update -->
|
|
149
|
+
<div v-once>
|
|
150
|
+
<span>Count: {{ count }}</span> <!-- count won't update! -->
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<!-- DON'T: When child components have their own reactive state -->
|
|
154
|
+
<div v-memo="[selected]">
|
|
155
|
+
<InputField v-model="item.name" /> <!-- v-model won't work properly -->
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<!-- DON'T: When the memoization benefit is minimal -->
|
|
159
|
+
<span v-once>{{ simpleText }}</span> <!-- Overhead not worth it -->
|
|
160
|
+
</template>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Performance Comparison
|
|
164
|
+
|
|
165
|
+
| Scenario | Without Directive | With v-once/v-memo |
|
|
166
|
+
|----------|-------------------|-------------------|
|
|
167
|
+
| Static header, parent re-renders 100x | Re-evaluated 100x | Evaluated 1x |
|
|
168
|
+
| 1000 items, selection changes | 1000 items re-render | 2 items re-render |
|
|
169
|
+
| Complex child component | Full re-render | Skipped if memoized |
|
|
170
|
+
|
|
171
|
+
## Debugging Memoized Components
|
|
172
|
+
|
|
173
|
+
```vue
|
|
174
|
+
<script setup>
|
|
175
|
+
import { onUpdated } from 'vue'
|
|
176
|
+
|
|
177
|
+
// This won't fire if v-memo prevents update
|
|
178
|
+
onUpdated(() => {
|
|
179
|
+
console.log('Component updated')
|
|
180
|
+
})
|
|
181
|
+
</script>
|
|
182
|
+
```
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Virtualize Large Lists to Avoid DOM Overload
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Rendering thousands of list items creates excessive DOM nodes, causing slow renders and high memory usage
|
|
5
|
+
type: efficiency
|
|
6
|
+
tags: [vue3, performance, virtual-list, large-data, dom, optimization]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Virtualize Large Lists to Avoid DOM Overload
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - Rendering all items in a large list (hundreds or thousands) creates massive amounts of DOM nodes. Each node consumes memory, slows down initial render, and makes updates expensive. List virtualization only renders visible items, dramatically improving performance.
|
|
12
|
+
|
|
13
|
+
Use a virtualization library when dealing with lists that could exceed 50-100 items, especially if items have complex content.
|
|
14
|
+
|
|
15
|
+
## Task List
|
|
16
|
+
|
|
17
|
+
- Identify lists that render more than 50-100 items
|
|
18
|
+
- Install a virtualization library (vue-virtual-scroller, @tanstack/vue-virtual)
|
|
19
|
+
- Replace standard `v-for` with virtualized component
|
|
20
|
+
- Ensure list items have consistent or estimable heights
|
|
21
|
+
- Test with realistic data volumes during development
|
|
22
|
+
|
|
23
|
+
## Recommended Libraries
|
|
24
|
+
|
|
25
|
+
| Library | Best For | Notes |
|
|
26
|
+
|---------|----------|-------|
|
|
27
|
+
| `vue-virtual-scroller` | General use, easy setup | Most popular, good defaults |
|
|
28
|
+
| `@tanstack/vue-virtual` | Complex layouts, headless | Framework-agnostic, flexible |
|
|
29
|
+
| `vue-virtual-scroll-grid` | Grid layouts | 2D virtualization |
|
|
30
|
+
| `vueuc/VVirtualList` | Naive UI projects | Part of Naive UI ecosystem |
|
|
31
|
+
|
|
32
|
+
**BAD:**
|
|
33
|
+
```vue
|
|
34
|
+
<template>
|
|
35
|
+
<!-- BAD: Renders ALL 10,000 items immediately -->
|
|
36
|
+
<div class="user-list">
|
|
37
|
+
<UserCard
|
|
38
|
+
v-for="user in users"
|
|
39
|
+
:key="user.id"
|
|
40
|
+
:user="user"
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<script setup>
|
|
46
|
+
import { ref, onMounted } from 'vue'
|
|
47
|
+
import UserCard from './UserCard.vue'
|
|
48
|
+
|
|
49
|
+
const users = ref([])
|
|
50
|
+
|
|
51
|
+
onMounted(async () => {
|
|
52
|
+
// 10,000 DOM nodes created, browser struggles
|
|
53
|
+
users.value = await fetchAllUsers()
|
|
54
|
+
})
|
|
55
|
+
</script>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**GOOD:**
|
|
59
|
+
```vue
|
|
60
|
+
<template>
|
|
61
|
+
<!-- GOOD: Only renders ~20 visible items at a time -->
|
|
62
|
+
<RecycleScroller
|
|
63
|
+
class="user-list"
|
|
64
|
+
:items="users"
|
|
65
|
+
:item-size="80"
|
|
66
|
+
key-field="id"
|
|
67
|
+
v-slot="{ item }"
|
|
68
|
+
>
|
|
69
|
+
<UserCard :user="item" />
|
|
70
|
+
</RecycleScroller>
|
|
71
|
+
</template>
|
|
72
|
+
|
|
73
|
+
<script setup>
|
|
74
|
+
import { ref, onMounted } from 'vue'
|
|
75
|
+
import { RecycleScroller } from 'vue-virtual-scroller'
|
|
76
|
+
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
|
77
|
+
import UserCard from './UserCard.vue'
|
|
78
|
+
|
|
79
|
+
const users = ref([])
|
|
80
|
+
|
|
81
|
+
onMounted(async () => {
|
|
82
|
+
// 10,000 items in memory, but only ~20 DOM nodes
|
|
83
|
+
users.value = await fetchAllUsers()
|
|
84
|
+
})
|
|
85
|
+
</script>
|
|
86
|
+
|
|
87
|
+
<style scoped>
|
|
88
|
+
.user-list {
|
|
89
|
+
height: 600px; /* Container must have fixed height */
|
|
90
|
+
}
|
|
91
|
+
</style>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Using @tanstack/vue-virtual
|
|
95
|
+
|
|
96
|
+
```vue
|
|
97
|
+
<template>
|
|
98
|
+
<div ref="parentRef" class="list-container">
|
|
99
|
+
<div
|
|
100
|
+
:style="{
|
|
101
|
+
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
102
|
+
position: 'relative'
|
|
103
|
+
}"
|
|
104
|
+
>
|
|
105
|
+
<div
|
|
106
|
+
v-for="virtualRow in rowVirtualizer.getVirtualItems()"
|
|
107
|
+
:key="virtualRow.key"
|
|
108
|
+
:style="{
|
|
109
|
+
position: 'absolute',
|
|
110
|
+
top: 0,
|
|
111
|
+
left: 0,
|
|
112
|
+
width: '100%',
|
|
113
|
+
height: `${virtualRow.size}px`,
|
|
114
|
+
transform: `translateY(${virtualRow.start}px)`
|
|
115
|
+
}"
|
|
116
|
+
>
|
|
117
|
+
<UserCard :user="users[virtualRow.index]" />
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</template>
|
|
122
|
+
|
|
123
|
+
<script setup>
|
|
124
|
+
import { ref } from 'vue'
|
|
125
|
+
import { useVirtualizer } from '@tanstack/vue-virtual'
|
|
126
|
+
|
|
127
|
+
const users = ref([/* 10,000 users */])
|
|
128
|
+
const parentRef = ref(null)
|
|
129
|
+
|
|
130
|
+
const rowVirtualizer = useVirtualizer({
|
|
131
|
+
count: users.value.length,
|
|
132
|
+
getScrollElement: () => parentRef.value,
|
|
133
|
+
estimateSize: () => 80, // Estimated row height
|
|
134
|
+
overscan: 5 // Render 5 extra items above/below viewport
|
|
135
|
+
})
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
<style scoped>
|
|
139
|
+
.list-container {
|
|
140
|
+
height: 600px;
|
|
141
|
+
overflow: auto;
|
|
142
|
+
}
|
|
143
|
+
</style>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Dynamic Heights with vue-virtual-scroller
|
|
147
|
+
|
|
148
|
+
```vue
|
|
149
|
+
<template>
|
|
150
|
+
<!-- For variable height items, use DynamicScroller -->
|
|
151
|
+
<DynamicScroller
|
|
152
|
+
:items="messages"
|
|
153
|
+
:min-item-size="54"
|
|
154
|
+
key-field="id"
|
|
155
|
+
>
|
|
156
|
+
<template #default="{ item, index, active }">
|
|
157
|
+
<DynamicScrollerItem
|
|
158
|
+
:item="item"
|
|
159
|
+
:active="active"
|
|
160
|
+
:data-index="index"
|
|
161
|
+
>
|
|
162
|
+
<ChatMessage :message="item" />
|
|
163
|
+
</DynamicScrollerItem>
|
|
164
|
+
</template>
|
|
165
|
+
</DynamicScroller>
|
|
166
|
+
</template>
|
|
167
|
+
|
|
168
|
+
<script setup>
|
|
169
|
+
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller'
|
|
170
|
+
</script>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Performance Comparison
|
|
174
|
+
|
|
175
|
+
| Approach | 100 Items | 1,000 Items | 10,000 Items |
|
|
176
|
+
|----------|-----------|-------------|--------------|
|
|
177
|
+
| Regular v-for | ~100 DOM nodes | ~1,000 DOM nodes | ~10,000 DOM nodes |
|
|
178
|
+
| Virtualized | ~20 DOM nodes | ~20 DOM nodes | ~20 DOM nodes |
|
|
179
|
+
| Initial render | Fast | Slow | Very slow / crashes |
|
|
180
|
+
| Virtualized render | Fast | Fast | Fast |
|
|
181
|
+
|
|
182
|
+
## When NOT to Virtualize
|
|
183
|
+
|
|
184
|
+
- Lists under 50 items with simple content
|
|
185
|
+
- Lists where all items must be accessible to screen readers simultaneously
|
|
186
|
+
- Print layouts where all content must render
|
|
187
|
+
- SEO-critical content that must be in initial HTML
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Vue Plugin Best Practices
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Incorrect plugin structure or injection key strategy causes install failures, collisions, and unsafe APIs
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, plugins, provide-inject, typescript, dependency-injection]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Vue Plugin Best Practices
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Vue plugins should follow the `app.use()` contract, expose explicit capabilities, and use collision-safe injection keys. This keeps plugin setup predictable and composable across large apps.
|
|
12
|
+
|
|
13
|
+
## Task List
|
|
14
|
+
|
|
15
|
+
- Export plugins as an object with `install()` or as an install function
|
|
16
|
+
- Use the `app` instance in `install()` to register components/directives/provides
|
|
17
|
+
- Type plugin APIs with `Plugin` (and options tuple types when needed)
|
|
18
|
+
- Use symbol keys (prefer `InjectionKey<T>`) for `provide/inject` in plugins
|
|
19
|
+
- Add a small typed composable wrapper for required injections to fail fast
|
|
20
|
+
|
|
21
|
+
## Structure Plugins for `app.use()`
|
|
22
|
+
|
|
23
|
+
A Vue plugin must be either:
|
|
24
|
+
- An object with `install(app, options?)`
|
|
25
|
+
- A function with the same signature
|
|
26
|
+
|
|
27
|
+
**BAD:**
|
|
28
|
+
```ts
|
|
29
|
+
const notAPlugin = {
|
|
30
|
+
doSomething() {}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
app.use(notAPlugin)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**GOOD:**
|
|
37
|
+
```ts
|
|
38
|
+
import type { App } from 'vue'
|
|
39
|
+
|
|
40
|
+
interface PluginOptions {
|
|
41
|
+
prefix?: string
|
|
42
|
+
debug?: boolean
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const myPlugin = {
|
|
46
|
+
install(app: App, options: PluginOptions = {}) {
|
|
47
|
+
const { prefix = 'my', debug = false } = options
|
|
48
|
+
|
|
49
|
+
if (debug) {
|
|
50
|
+
console.log('Installing myPlugin with prefix:', prefix)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
app.provide('myPlugin', { prefix })
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
app.use(myPlugin, { prefix: 'custom', debug: true })
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**GOOD:**
|
|
61
|
+
```ts
|
|
62
|
+
import type { App } from 'vue'
|
|
63
|
+
|
|
64
|
+
function simplePlugin(app: App, options?: { message: string }) {
|
|
65
|
+
app.config.globalProperties.$greet = () => options?.message ?? 'Hello!'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
app.use(simplePlugin, { message: 'Welcome!' })
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Register Capabilities Explicitly in `install()`
|
|
72
|
+
|
|
73
|
+
Inside `install()`, wire behavior through Vue application APIs:
|
|
74
|
+
- `app.component()` for global components
|
|
75
|
+
- `app.directive()` for global directives
|
|
76
|
+
- `app.provide()` for injectable services and config
|
|
77
|
+
- `app.config.globalProperties` for optional global helpers (sparingly)
|
|
78
|
+
|
|
79
|
+
**BAD:**
|
|
80
|
+
```ts
|
|
81
|
+
const uselessPlugin = {
|
|
82
|
+
install(app, options) {
|
|
83
|
+
const service = createService(options)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**GOOD:**
|
|
89
|
+
```ts
|
|
90
|
+
const usefulPlugin = {
|
|
91
|
+
install(app, options) {
|
|
92
|
+
const service = createService(options)
|
|
93
|
+
app.provide(serviceKey, service)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Type Plugin Contracts
|
|
99
|
+
|
|
100
|
+
Use Vue's `Plugin` type to keep install signatures and options type-safe.
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import type { App, Plugin } from 'vue'
|
|
104
|
+
|
|
105
|
+
interface MyOptions {
|
|
106
|
+
apiKey: string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const myPlugin: Plugin<[MyOptions]> = {
|
|
110
|
+
install(app: App, options: MyOptions) {
|
|
111
|
+
app.provide(apiKeyKey, options.apiKey)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Use Symbol Injection Keys in Plugins
|
|
117
|
+
|
|
118
|
+
String keys can collide (`'http'`, `'config'`, `'i18n'`). Use symbol keys with `InjectionKey<T>` so injections are unique and typed.
|
|
119
|
+
|
|
120
|
+
**BAD:**
|
|
121
|
+
```ts
|
|
122
|
+
export default {
|
|
123
|
+
install(app) {
|
|
124
|
+
app.provide('http', axios)
|
|
125
|
+
app.provide('config', appConfig)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**GOOD:**
|
|
131
|
+
```ts
|
|
132
|
+
import type { InjectionKey } from 'vue'
|
|
133
|
+
import type { AxiosInstance } from 'axios'
|
|
134
|
+
|
|
135
|
+
interface AppConfig {
|
|
136
|
+
apiUrl: string
|
|
137
|
+
timeout: number
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const httpKey: InjectionKey<AxiosInstance> = Symbol('http')
|
|
141
|
+
export const configKey: InjectionKey<AppConfig> = Symbol('appConfig')
|
|
142
|
+
|
|
143
|
+
export default {
|
|
144
|
+
install(app) {
|
|
145
|
+
app.provide(httpKey, axios)
|
|
146
|
+
app.provide(configKey, { apiUrl: '/api', timeout: 5000 })
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Provide Required Injection Helpers
|
|
152
|
+
|
|
153
|
+
Wrap required injections in composables that throw clear setup errors.
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { inject } from 'vue'
|
|
157
|
+
import { authKey, type AuthService } from '@/injection-keys'
|
|
158
|
+
|
|
159
|
+
export function useAuth(): AuthService {
|
|
160
|
+
const auth = inject(authKey)
|
|
161
|
+
if (!auth) {
|
|
162
|
+
throw new Error('Auth plugin not installed. Did you forget app.use(authPlugin)?')
|
|
163
|
+
}
|
|
164
|
+
return auth
|
|
165
|
+
}
|
|
166
|
+
```
|