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,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Simple Hash Routing Requires Event Listener Cleanup
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: When implementing basic routing without Vue Router, forgetting to remove hashchange listeners causes memory leaks and multiple handler execution
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, routing, events, memory-leak, cleanup]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Simple Hash Routing Requires Event Listener Cleanup
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - When implementing basic client-side routing without Vue Router (using hash-based routing with `hashchange` events), you must clean up event listeners when the component unmounts. Failure to do so causes memory leaks and can result in multiple handlers firing after the component is recreated.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Store event listener reference for cleanup
|
|
16
|
+
- [ ] Use onUnmounted to remove event listener
|
|
17
|
+
- [ ] Consider using Vue Router instead for production apps
|
|
18
|
+
- [ ] Test component mount/unmount cycles
|
|
19
|
+
|
|
20
|
+
## The Problem
|
|
21
|
+
|
|
22
|
+
```vue
|
|
23
|
+
<script setup>
|
|
24
|
+
import { ref, computed } from 'vue'
|
|
25
|
+
import Home from './Home.vue'
|
|
26
|
+
import About from './About.vue'
|
|
27
|
+
|
|
28
|
+
const routes = {
|
|
29
|
+
'/': Home,
|
|
30
|
+
'/about': About
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const currentPath = ref(window.location.hash)
|
|
34
|
+
|
|
35
|
+
// BUG: Event listener is never removed!
|
|
36
|
+
// Each time this component mounts, a NEW listener is added
|
|
37
|
+
// After mounting 5 times, you have 5 listeners running
|
|
38
|
+
window.addEventListener('hashchange', () => {
|
|
39
|
+
currentPath.value = window.location.hash
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const currentView = computed(() => {
|
|
43
|
+
return routes[currentPath.value.slice(1) || '/']
|
|
44
|
+
})
|
|
45
|
+
</script>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**What happens:**
|
|
49
|
+
1. Component mounts, adds listener
|
|
50
|
+
2. Component unmounts (e.g., route change, v-if toggle)
|
|
51
|
+
3. Component mounts again, adds ANOTHER listener
|
|
52
|
+
4. Now TWO listeners respond to each hash change
|
|
53
|
+
5. Eventually causes performance issues and memory leaks
|
|
54
|
+
|
|
55
|
+
## Solution: Proper Cleanup with onUnmounted
|
|
56
|
+
|
|
57
|
+
```vue
|
|
58
|
+
<script setup>
|
|
59
|
+
import { ref, computed, onUnmounted } from 'vue'
|
|
60
|
+
import Home from './Home.vue'
|
|
61
|
+
import About from './About.vue'
|
|
62
|
+
import NotFound from './NotFound.vue'
|
|
63
|
+
|
|
64
|
+
const routes = {
|
|
65
|
+
'/': Home,
|
|
66
|
+
'/about': About
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const currentPath = ref(window.location.hash)
|
|
70
|
+
|
|
71
|
+
// Store handler reference for cleanup
|
|
72
|
+
function handleHashChange() {
|
|
73
|
+
currentPath.value = window.location.hash
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Add listener
|
|
77
|
+
window.addEventListener('hashchange', handleHashChange)
|
|
78
|
+
|
|
79
|
+
// CRITICAL: Remove listener on unmount
|
|
80
|
+
onUnmounted(() => {
|
|
81
|
+
window.removeEventListener('hashchange', handleHashChange)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const currentView = computed(() => {
|
|
85
|
+
return routes[currentPath.value.slice(1) || '/'] || NotFound
|
|
86
|
+
})
|
|
87
|
+
</script>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Solution: Using Options API
|
|
91
|
+
|
|
92
|
+
```vue
|
|
93
|
+
<script>
|
|
94
|
+
import Home from './Home.vue'
|
|
95
|
+
import About from './About.vue'
|
|
96
|
+
import NotFound from './NotFound.vue'
|
|
97
|
+
|
|
98
|
+
const routes = {
|
|
99
|
+
'/': Home,
|
|
100
|
+
'/about': About
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default {
|
|
104
|
+
data() {
|
|
105
|
+
return {
|
|
106
|
+
currentPath: window.location.hash
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
computed: {
|
|
111
|
+
currentView() {
|
|
112
|
+
return routes[this.currentPath.slice(1) || '/'] || NotFound
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
mounted() {
|
|
117
|
+
// Store bound handler for cleanup
|
|
118
|
+
this.hashHandler = () => {
|
|
119
|
+
this.currentPath = window.location.hash
|
|
120
|
+
}
|
|
121
|
+
window.addEventListener('hashchange', this.hashHandler)
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
beforeUnmount() {
|
|
125
|
+
// Clean up
|
|
126
|
+
window.removeEventListener('hashchange', this.hashHandler)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
</script>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Solution: Composable for Reusable Hash Routing
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
// composables/useHashRouter.js
|
|
136
|
+
import { ref, computed, onUnmounted } from 'vue'
|
|
137
|
+
|
|
138
|
+
export function useHashRouter(routes, notFoundComponent = null) {
|
|
139
|
+
const currentPath = ref(window.location.hash)
|
|
140
|
+
|
|
141
|
+
function handleHashChange() {
|
|
142
|
+
currentPath.value = window.location.hash
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Setup
|
|
146
|
+
window.addEventListener('hashchange', handleHashChange)
|
|
147
|
+
|
|
148
|
+
// Cleanup - handled automatically when component unmounts
|
|
149
|
+
onUnmounted(() => {
|
|
150
|
+
window.removeEventListener('hashchange', handleHashChange)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
const currentView = computed(() => {
|
|
154
|
+
const path = currentPath.value.slice(1) || '/'
|
|
155
|
+
return routes[path] || notFoundComponent
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
function navigate(path) {
|
|
159
|
+
window.location.hash = path
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
currentPath,
|
|
164
|
+
currentView,
|
|
165
|
+
navigate
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```vue
|
|
171
|
+
<!-- Usage -->
|
|
172
|
+
<script setup>
|
|
173
|
+
import { useHashRouter } from '@/composables/useHashRouter'
|
|
174
|
+
import Home from './Home.vue'
|
|
175
|
+
import About from './About.vue'
|
|
176
|
+
import NotFound from './NotFound.vue'
|
|
177
|
+
|
|
178
|
+
const { currentView } = useHashRouter({
|
|
179
|
+
'/': Home,
|
|
180
|
+
'/about': About
|
|
181
|
+
}, NotFound)
|
|
182
|
+
</script>
|
|
183
|
+
|
|
184
|
+
<template>
|
|
185
|
+
<component :is="currentView" />
|
|
186
|
+
</template>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## When to Use Simple Routing vs Vue Router
|
|
190
|
+
|
|
191
|
+
| Use Simple Hash Routing | Use Vue Router |
|
|
192
|
+
|------------------------|----------------|
|
|
193
|
+
| Learning/prototyping | Production apps |
|
|
194
|
+
| Very simple apps (2-3 pages) | Nested routes needed |
|
|
195
|
+
| No build step available | Navigation guards needed |
|
|
196
|
+
| Bundle size critical | Lazy loading needed |
|
|
197
|
+
| Static hosting only | History mode (clean URLs) |
|
|
198
|
+
|
|
199
|
+
## Key Points
|
|
200
|
+
|
|
201
|
+
1. **Always clean up event listeners** - Use onUnmounted or beforeUnmount
|
|
202
|
+
2. **Store handler reference** - Anonymous functions can't be removed
|
|
203
|
+
3. **Consider Vue Router for real apps** - It handles cleanup automatically
|
|
204
|
+
4. **Test unmount scenarios** - v-if toggling, hot module replacement
|
|
205
|
+
5. **Composables help encapsulate cleanup logic** - Reusable and automatic
|
|
206
|
+
|
|
207
|
+
## Reference
|
|
208
|
+
- [Vue.js Routing Documentation](https://vuejs.org/guide/scaling-up/routing.html)
|
|
209
|
+
- [Vue Router Official Library](https://router.vuejs.org/)
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Vue Router Library for Production Applications
|
|
3
|
+
impact: LOW
|
|
4
|
+
impactDescription: Simple hash routing lacks essential features for production SPAs; Vue Router provides navigation guards, lazy loading, and proper history management
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, vue-router, spa, production, architecture]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Use Vue Router Library for Production Applications
|
|
10
|
+
|
|
11
|
+
**Impact: LOW** - While you can implement basic routing with hash changes and dynamic components, the official Vue Router library should be used for any production single-page application. It provides essential features like navigation guards, nested routes, lazy loading, and proper browser history integration that are tedious and error-prone to implement manually.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Install Vue Router for production SPAs
|
|
16
|
+
- [ ] Use simple routing only for learning or tiny prototypes
|
|
17
|
+
- [ ] Leverage built-in features: guards, lazy loading, meta fields
|
|
18
|
+
- [ ] Consider router-based state and data loading patterns
|
|
19
|
+
|
|
20
|
+
## When Simple Routing is Acceptable
|
|
21
|
+
|
|
22
|
+
```vue
|
|
23
|
+
<!-- Only for: learning, prototypes, or micro-apps with 2-3 pages -->
|
|
24
|
+
<script setup>
|
|
25
|
+
import { ref, computed } from 'vue'
|
|
26
|
+
import Home from './Home.vue'
|
|
27
|
+
import About from './About.vue'
|
|
28
|
+
|
|
29
|
+
const routes = { '/': Home, '/about': About }
|
|
30
|
+
const currentPath = ref(window.location.hash.slice(1) || '/')
|
|
31
|
+
|
|
32
|
+
window.addEventListener('hashchange', () => {
|
|
33
|
+
currentPath.value = window.location.hash.slice(1) || '/'
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const currentView = computed(() => routes[currentPath.value])
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<template>
|
|
40
|
+
<nav>
|
|
41
|
+
<a href="#/">Home</a>
|
|
42
|
+
<a href="#/about">About</a>
|
|
43
|
+
</nav>
|
|
44
|
+
<component :is="currentView" />
|
|
45
|
+
</template>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Why Vue Router for Production
|
|
49
|
+
|
|
50
|
+
### Features You'd Have to Implement Manually
|
|
51
|
+
|
|
52
|
+
| Feature | Simple Routing | Vue Router |
|
|
53
|
+
|---------|---------------|------------|
|
|
54
|
+
| Navigation guards | Manual, error-prone | Built-in, composable |
|
|
55
|
+
| Nested routes | Complex to implement | Native support |
|
|
56
|
+
| Route params | Parse manually | Automatic extraction |
|
|
57
|
+
| Lazy loading | DIY with dynamic imports | Built-in with code splitting |
|
|
58
|
+
| History mode (clean URLs) | Requires server config + manual | Built-in |
|
|
59
|
+
| Scroll behavior | Manual | Configurable |
|
|
60
|
+
| Route transitions | DIY | Integrated with Transition |
|
|
61
|
+
| Active link styling | Manual class toggling | `router-link-active` class |
|
|
62
|
+
| Programmatic navigation | `location.hash = ...` | `router.push()`, `router.replace()` |
|
|
63
|
+
| Route meta fields | N/A | Built-in |
|
|
64
|
+
|
|
65
|
+
## Production Setup with Vue Router
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
// router/index.js
|
|
69
|
+
import { createRouter, createWebHistory } from 'vue-router'
|
|
70
|
+
|
|
71
|
+
const routes = [
|
|
72
|
+
{
|
|
73
|
+
path: '/',
|
|
74
|
+
name: 'Home',
|
|
75
|
+
component: () => import('@/views/Home.vue'), // Lazy loaded
|
|
76
|
+
meta: { requiresAuth: false }
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
path: '/dashboard',
|
|
80
|
+
name: 'Dashboard',
|
|
81
|
+
component: () => import('@/views/Dashboard.vue'),
|
|
82
|
+
meta: { requiresAuth: true },
|
|
83
|
+
children: [
|
|
84
|
+
{
|
|
85
|
+
path: 'settings',
|
|
86
|
+
name: 'Settings',
|
|
87
|
+
component: () => import('@/views/Settings.vue')
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
path: '/users/:id',
|
|
93
|
+
name: 'UserProfile',
|
|
94
|
+
component: () => import('@/views/UserProfile.vue'),
|
|
95
|
+
props: true // Pass params as props
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
path: '/:pathMatch(.*)*',
|
|
99
|
+
name: 'NotFound',
|
|
100
|
+
component: () => import('@/views/NotFound.vue')
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
const router = createRouter({
|
|
105
|
+
history: createWebHistory(),
|
|
106
|
+
routes,
|
|
107
|
+
scrollBehavior(to, from, savedPosition) {
|
|
108
|
+
return savedPosition || { top: 0 }
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// Global navigation guard
|
|
113
|
+
router.beforeEach((to, from) => {
|
|
114
|
+
if (to.meta.requiresAuth && !isAuthenticated()) {
|
|
115
|
+
return { name: 'Login', query: { redirect: to.fullPath } }
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
export default router
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
// main.js
|
|
124
|
+
import { createApp } from 'vue'
|
|
125
|
+
import App from './App.vue'
|
|
126
|
+
import router from './router'
|
|
127
|
+
|
|
128
|
+
createApp(App)
|
|
129
|
+
.use(router)
|
|
130
|
+
.mount('#app')
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```vue
|
|
134
|
+
<!-- App.vue -->
|
|
135
|
+
<template>
|
|
136
|
+
<nav>
|
|
137
|
+
<router-link to="/">Home</router-link>
|
|
138
|
+
<router-link to="/dashboard">Dashboard</router-link>
|
|
139
|
+
</nav>
|
|
140
|
+
|
|
141
|
+
<router-view v-slot="{ Component }">
|
|
142
|
+
<transition name="fade" mode="out-in">
|
|
143
|
+
<component :is="Component" />
|
|
144
|
+
</transition>
|
|
145
|
+
</router-view>
|
|
146
|
+
</template>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Modern Vue Router Features (2025+)
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
// Data Loading API (Vue Router 4.2+)
|
|
153
|
+
const routes = [
|
|
154
|
+
{
|
|
155
|
+
path: '/users/:id',
|
|
156
|
+
component: UserProfile,
|
|
157
|
+
// Load data at route level
|
|
158
|
+
loader: async (route) => {
|
|
159
|
+
return { user: await fetchUser(route.params.id) }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
// View Transitions API integration
|
|
165
|
+
const router = createRouter({
|
|
166
|
+
// Enable native browser view transitions
|
|
167
|
+
// Requires browser support (Chrome 111+)
|
|
168
|
+
})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Key Points
|
|
172
|
+
|
|
173
|
+
1. **Use Vue Router for any app beyond a prototype** - The features are essential
|
|
174
|
+
2. **Simple routing is for learning** - Understand the concepts, then use the library
|
|
175
|
+
3. **Lazy loading is critical for bundle size** - Vue Router makes it trivial
|
|
176
|
+
4. **Navigation guards prevent security issues** - Hard to get right manually
|
|
177
|
+
5. **History mode requires Vue Router** - Clean URLs need proper handling
|
|
178
|
+
6. **New features keep coming** - Data Loading API, View Transitions
|
|
179
|
+
|
|
180
|
+
## Reference
|
|
181
|
+
- [Vue.js Routing Guide](https://vuejs.org/guide/scaling-up/routing.html)
|
|
182
|
+
- [Vue Router Documentation](https://router.vuejs.org/)
|
|
183
|
+
- [Vue Router Getting Started](https://router.vuejs.org/guide/)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 hyf0, SerKo <https://github.com/serkodev>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vue-testing-best-practices
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
license: MIT
|
|
5
|
+
author: github.com/vuejs-ai
|
|
6
|
+
description: Use for Vue.js testing. Covers Vitest, Vue Test Utils, component testing, mocking, testing patterns, and Playwright for E2E testing.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Vue.js testing best practices, patterns, and common gotchas.
|
|
10
|
+
|
|
11
|
+
### Testing
|
|
12
|
+
- Setting up test infrastructure for Vue 3 projects → See [testing-vitest-recommended-for-vue](reference/testing-vitest-recommended-for-vue.md)
|
|
13
|
+
- Tests keep breaking when refactoring component internals → See [testing-component-blackbox-approach](reference/testing-component-blackbox-approach.md)
|
|
14
|
+
- Tests fail intermittently with race conditions → See [testing-async-await-flushpromises](reference/testing-async-await-flushpromises.md)
|
|
15
|
+
- Composables using lifecycle hooks or inject fail to test → See [testing-composables-helper-wrapper](reference/testing-composables-helper-wrapper.md)
|
|
16
|
+
- Getting "injection Symbol(pinia) not found" errors in tests → See [testing-pinia-store-setup](reference/testing-pinia-store-setup.md)
|
|
17
|
+
- Components with async setup won't render in tests → See [testing-suspense-async-components](reference/testing-suspense-async-components.md)
|
|
18
|
+
- Snapshot tests keep passing despite broken functionality → See [testing-no-snapshot-only](reference/testing-no-snapshot-only.md)
|
|
19
|
+
- Choosing end-to-end testing framework for Vue apps → See [testing-e2e-playwright-recommended](reference/testing-e2e-playwright-recommended.md)
|
|
20
|
+
- Tests need to verify computed styles or real DOM events → See [testing-browser-vs-node-runners](reference/testing-browser-vs-node-runners.md)
|
|
21
|
+
- Testing components created with defineAsyncComponent fails → See [async-component-testing](reference/async-component-testing.md)
|
|
22
|
+
- Teleported modal content can't be found in wrapper queries → See [teleport-testing-complexity](reference/teleport-testing-complexity.md)
|
|
23
|
+
|
|
24
|
+
## Reference
|
|
25
|
+
|
|
26
|
+
- [Vue.js Testing Guide](https://vuejs.org/guide/scaling-up/testing)
|
|
27
|
+
- [Vue Test Utils](https://test-utils.vuejs.org/)
|
|
28
|
+
- [Vitest Documentation](https://vitest.dev/)
|
|
29
|
+
- [Playwright Documentation](https://playwright.dev/)
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use flushPromises for Testing Async Components
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: Without awaiting async operations, tests make assertions before the component has rendered, causing false negatives
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, testing, async, defineAsyncComponent, flushPromises, vitest]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Use flushPromises for Testing Async Components
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - When testing async components created with `defineAsyncComponent`, you must use `await flushPromises()` to ensure the component has loaded before making assertions. Vue updates asynchronously, so tests that don't account for this will make assertions before the component has rendered.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Use `async/await` in test functions for async components
|
|
16
|
+
- [ ] Call `await flushPromises()` after mounting async components
|
|
17
|
+
- [ ] Test loading states by making assertions before `flushPromises()`
|
|
18
|
+
- [ ] Test error states using rejected promises in `defineAsyncComponent`
|
|
19
|
+
- [ ] Use `trigger()` with `await` as it returns a Promise
|
|
20
|
+
|
|
21
|
+
**Incorrect:**
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
import { mount } from '@vue/test-utils'
|
|
25
|
+
import { defineAsyncComponent } from 'vue'
|
|
26
|
+
|
|
27
|
+
const AsyncWidget = defineAsyncComponent(() =>
|
|
28
|
+
import('./Widget.vue')
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
test('renders async component', () => {
|
|
32
|
+
const wrapper = mount(AsyncWidget)
|
|
33
|
+
|
|
34
|
+
// FAILS: Component hasn't loaded yet
|
|
35
|
+
expect(wrapper.text()).toContain('Widget Content')
|
|
36
|
+
})
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Correct:**
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
import { mount, flushPromises } from '@vue/test-utils'
|
|
43
|
+
import { defineAsyncComponent, nextTick } from 'vue'
|
|
44
|
+
|
|
45
|
+
const AsyncWidget = defineAsyncComponent(() =>
|
|
46
|
+
import('./Widget.vue')
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
test('renders async component', async () => {
|
|
50
|
+
const wrapper = mount(AsyncWidget)
|
|
51
|
+
|
|
52
|
+
// Wait for async component to load
|
|
53
|
+
await flushPromises()
|
|
54
|
+
|
|
55
|
+
expect(wrapper.text()).toContain('Widget Content')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('shows loading state initially', async () => {
|
|
59
|
+
const AsyncWithLoading = defineAsyncComponent({
|
|
60
|
+
loader: () => import('./Widget.vue'),
|
|
61
|
+
loadingComponent: { template: '<div>Loading...</div>' },
|
|
62
|
+
delay: 0
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const wrapper = mount(AsyncWithLoading)
|
|
66
|
+
|
|
67
|
+
// Check loading state immediately
|
|
68
|
+
expect(wrapper.text()).toContain('Loading...')
|
|
69
|
+
|
|
70
|
+
// Wait for component to load
|
|
71
|
+
await flushPromises()
|
|
72
|
+
|
|
73
|
+
// Check final state
|
|
74
|
+
expect(wrapper.text()).toContain('Widget Content')
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Testing with Suspense
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
import { mount, flushPromises } from '@vue/test-utils'
|
|
82
|
+
import { Suspense, defineAsyncComponent, h } from 'vue'
|
|
83
|
+
|
|
84
|
+
const AsyncWidget = defineAsyncComponent(() =>
|
|
85
|
+
import('./Widget.vue')
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
test('renders async component with Suspense', async () => {
|
|
89
|
+
const wrapper = mount({
|
|
90
|
+
components: { AsyncWidget },
|
|
91
|
+
template: `
|
|
92
|
+
<Suspense>
|
|
93
|
+
<AsyncWidget />
|
|
94
|
+
<template #fallback>
|
|
95
|
+
<div>Loading...</div>
|
|
96
|
+
</template>
|
|
97
|
+
</Suspense>
|
|
98
|
+
`
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
// Initially shows fallback
|
|
102
|
+
expect(wrapper.text()).toContain('Loading...')
|
|
103
|
+
|
|
104
|
+
// Wait for async resolution
|
|
105
|
+
await flushPromises()
|
|
106
|
+
|
|
107
|
+
// Now shows actual content
|
|
108
|
+
expect(wrapper.text()).toContain('Widget Content')
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Testing Error States
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
import { mount, flushPromises } from '@vue/test-utils'
|
|
116
|
+
import { defineAsyncComponent } from 'vue'
|
|
117
|
+
|
|
118
|
+
test('shows error component on load failure', async () => {
|
|
119
|
+
const AsyncWithError = defineAsyncComponent({
|
|
120
|
+
loader: () => Promise.reject(new Error('Failed to load')),
|
|
121
|
+
errorComponent: { template: '<div>Error loading component</div>' }
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const wrapper = mount(AsyncWithError)
|
|
125
|
+
|
|
126
|
+
await flushPromises()
|
|
127
|
+
|
|
128
|
+
expect(wrapper.text()).toContain('Error loading component')
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Utilities Reference
|
|
133
|
+
|
|
134
|
+
| Utility | Purpose |
|
|
135
|
+
|---------|---------|
|
|
136
|
+
| `await flushPromises()` | Resolves all pending promises |
|
|
137
|
+
| `await nextTick()` | Waits for Vue's next DOM update cycle |
|
|
138
|
+
| `await wrapper.trigger('click')` | Triggers event and waits for update |
|
|
139
|
+
|
|
140
|
+
## Dynamic Import Handling
|
|
141
|
+
|
|
142
|
+
**Note:** Dynamic imports (`import('./File.vue')`) may require additional handling beyond `flushPromises()` in test environments. Test runners like Vitest handle module resolution differently than runtime bundlers, which can cause timing issues with dynamic imports. If `flushPromises()` alone doesn't resolve the component, consider:
|
|
143
|
+
|
|
144
|
+
- Mocking the dynamic import to return the component synchronously
|
|
145
|
+
- Using multiple `await flushPromises()` calls in sequence
|
|
146
|
+
- Wrapping assertions in `waitFor()` or retry utilities
|
|
147
|
+
- Configuring your test runner's module resolution settings
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
// If flushPromises() isn't sufficient, mock the import
|
|
151
|
+
vi.mock('./Widget.vue', () => ({
|
|
152
|
+
default: { template: '<div>Widget Content</div>' }
|
|
153
|
+
}))
|
|
154
|
+
|
|
155
|
+
// Or use multiple flush calls for nested async operations
|
|
156
|
+
await flushPromises()
|
|
157
|
+
await flushPromises()
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## References
|
|
161
|
+
|
|
162
|
+
- [Vue Test Utils - Asynchronous Behavior](https://test-utils.vuejs.org/guide/advanced/async-suspense)
|
|
163
|
+
- [Vue.js Async Components Documentation](https://vuejs.org/guide/components/async)
|