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,238 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use URL State for Shareable Filters Instead of Stores
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Storing filter/search state only in stores loses state on refresh and prevents link sharing
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, state-management, url, router, filters, ux, vue-router]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Use URL State for Shareable Filters Instead of Stores
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - Storing ephemeral UI state like filters, search queries, pagination, and sorting only in Pinia or component state means users lose that state on page refresh and cannot share links to specific filtered views. This hurts user experience and SEO.
|
|
12
|
+
|
|
13
|
+
For state that represents a "view" of data, use URL query parameters instead of or alongside stores.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Identify ephemeral UI state: filters, search, sort, pagination, tab selection
|
|
18
|
+
- [ ] Store such state in URL query parameters
|
|
19
|
+
- [ ] Sync URL state with component/store state bidirectionally
|
|
20
|
+
- [ ] Consider using VueUse's `useRouteQuery` for type-safe URL state
|
|
21
|
+
- [ ] Keep URL state minimal and human-readable
|
|
22
|
+
|
|
23
|
+
## The Problem: Store-Only State
|
|
24
|
+
|
|
25
|
+
```vue
|
|
26
|
+
<script setup>
|
|
27
|
+
import { ref } from 'vue'
|
|
28
|
+
import { useProductStore } from '@/stores/products'
|
|
29
|
+
|
|
30
|
+
const productStore = useProductStore()
|
|
31
|
+
|
|
32
|
+
// Filter state in component/store only
|
|
33
|
+
const selectedCategory = ref('all')
|
|
34
|
+
const priceRange = ref([0, 1000])
|
|
35
|
+
const sortBy = ref('newest')
|
|
36
|
+
const searchQuery = ref('')
|
|
37
|
+
|
|
38
|
+
// Problems with this approach:
|
|
39
|
+
// 1. User refreshes page → filters reset to defaults
|
|
40
|
+
// 2. User bookmarks page → bookmark doesn't include filter state
|
|
41
|
+
// 3. User shares link → friend sees unfiltered view
|
|
42
|
+
// 4. Back button doesn't restore previous filter state
|
|
43
|
+
</script>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## The Solution: URL-Based State
|
|
47
|
+
|
|
48
|
+
```vue
|
|
49
|
+
<script setup>
|
|
50
|
+
import { computed, watch } from 'vue'
|
|
51
|
+
import { useRoute, useRouter } from 'vue-router'
|
|
52
|
+
|
|
53
|
+
const route = useRoute()
|
|
54
|
+
const router = useRouter()
|
|
55
|
+
|
|
56
|
+
// Read filter state FROM URL
|
|
57
|
+
const selectedCategory = computed({
|
|
58
|
+
get: () => route.query.category || 'all',
|
|
59
|
+
set: (value) => updateQuery({ category: value === 'all' ? undefined : value })
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const sortBy = computed({
|
|
63
|
+
get: () => route.query.sort || 'newest',
|
|
64
|
+
set: (value) => updateQuery({ sort: value === 'newest' ? undefined : value })
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const searchQuery = computed({
|
|
68
|
+
get: () => route.query.q || '',
|
|
69
|
+
set: (value) => updateQuery({ q: value || undefined })
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const page = computed({
|
|
73
|
+
get: () => parseInt(route.query.page) || 1,
|
|
74
|
+
set: (value) => updateQuery({ page: value === 1 ? undefined : value })
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Helper to update URL without full navigation
|
|
78
|
+
function updateQuery(newParams) {
|
|
79
|
+
router.push({
|
|
80
|
+
query: {
|
|
81
|
+
...route.query,
|
|
82
|
+
...newParams
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<div>
|
|
90
|
+
<input v-model="searchQuery" placeholder="Search...">
|
|
91
|
+
|
|
92
|
+
<select v-model="selectedCategory">
|
|
93
|
+
<option value="all">All Categories</option>
|
|
94
|
+
<option value="electronics">Electronics</option>
|
|
95
|
+
<option value="clothing">Clothing</option>
|
|
96
|
+
</select>
|
|
97
|
+
|
|
98
|
+
<select v-model="sortBy">
|
|
99
|
+
<option value="newest">Newest</option>
|
|
100
|
+
<option value="price-low">Price: Low to High</option>
|
|
101
|
+
<option value="price-high">Price: High to Low</option>
|
|
102
|
+
</select>
|
|
103
|
+
|
|
104
|
+
<!-- URL now looks like: /products?category=electronics&sort=price-low&q=phone -->
|
|
105
|
+
<!-- Users can bookmark, share, and refresh without losing state -->
|
|
106
|
+
</div>
|
|
107
|
+
</template>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Using VueUse for Cleaner Code
|
|
111
|
+
|
|
112
|
+
VueUse provides `useRouteQuery` for type-safe URL state:
|
|
113
|
+
|
|
114
|
+
```vue
|
|
115
|
+
<script setup>
|
|
116
|
+
import { useRouteQuery } from '@vueuse/router'
|
|
117
|
+
|
|
118
|
+
// Automatically syncs with URL query parameters
|
|
119
|
+
const category = useRouteQuery('category', 'all')
|
|
120
|
+
const sort = useRouteQuery('sort', 'newest')
|
|
121
|
+
const search = useRouteQuery('q', '')
|
|
122
|
+
const page = useRouteQuery('page', 1, { transform: Number })
|
|
123
|
+
const showOutOfStock = useRouteQuery('inStock', false, { transform: Boolean })
|
|
124
|
+
|
|
125
|
+
// Arrays work too
|
|
126
|
+
const selectedTags = useRouteQuery('tags', [], {
|
|
127
|
+
transform: (v) => Array.isArray(v) ? v : v ? [v] : []
|
|
128
|
+
})
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<template>
|
|
132
|
+
<input v-model="search" placeholder="Search...">
|
|
133
|
+
<select v-model="category">...</select>
|
|
134
|
+
<input type="checkbox" v-model="showOutOfStock"> Show out of stock
|
|
135
|
+
</template>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Hybrid Approach: URL + Store
|
|
139
|
+
|
|
140
|
+
For complex state, sync URL with store:
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
// stores/productFilters.js
|
|
144
|
+
import { defineStore } from 'pinia'
|
|
145
|
+
import { useRoute, useRouter } from 'vue-router'
|
|
146
|
+
import { watch } from 'vue'
|
|
147
|
+
|
|
148
|
+
export const useProductFiltersStore = defineStore('productFilters', () => {
|
|
149
|
+
const route = useRoute()
|
|
150
|
+
const router = useRouter()
|
|
151
|
+
|
|
152
|
+
// Local reactive state
|
|
153
|
+
const category = ref('all')
|
|
154
|
+
const sortBy = ref('newest')
|
|
155
|
+
const searchQuery = ref('')
|
|
156
|
+
const page = ref(1)
|
|
157
|
+
|
|
158
|
+
// Initialize from URL on store creation
|
|
159
|
+
function initFromUrl() {
|
|
160
|
+
category.value = route.query.category || 'all'
|
|
161
|
+
sortBy.value = route.query.sort || 'newest'
|
|
162
|
+
searchQuery.value = route.query.q || ''
|
|
163
|
+
page.value = parseInt(route.query.page) || 1
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Sync state changes TO URL
|
|
167
|
+
function syncToUrl() {
|
|
168
|
+
router.replace({
|
|
169
|
+
query: {
|
|
170
|
+
category: category.value !== 'all' ? category.value : undefined,
|
|
171
|
+
sort: sortBy.value !== 'newest' ? sortBy.value : undefined,
|
|
172
|
+
q: searchQuery.value || undefined,
|
|
173
|
+
page: page.value > 1 ? page.value : undefined
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Watch for URL changes (back/forward navigation)
|
|
179
|
+
watch(() => route.query, initFromUrl, { immediate: true })
|
|
180
|
+
|
|
181
|
+
// Watch for state changes and sync to URL
|
|
182
|
+
watch([category, sortBy, searchQuery, page], syncToUrl)
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
category,
|
|
186
|
+
sortBy,
|
|
187
|
+
searchQuery,
|
|
188
|
+
page,
|
|
189
|
+
initFromUrl
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## What Goes in URL vs Store
|
|
195
|
+
|
|
196
|
+
| State Type | URL | Store | Notes |
|
|
197
|
+
|------------|-----|-------|-------|
|
|
198
|
+
| Filters | Yes | Optional | Shareable, bookmarkable |
|
|
199
|
+
| Search query | Yes | Optional | SEO benefit |
|
|
200
|
+
| Pagination | Yes | Optional | Deep linking |
|
|
201
|
+
| Sort order | Yes | Optional | User expectation |
|
|
202
|
+
| Selected tab | Yes | Optional | Deep linking |
|
|
203
|
+
| Modal open state | Maybe | Yes | Usually not shareable |
|
|
204
|
+
| Form draft | No | Yes | Private, temporary |
|
|
205
|
+
| User session | No | Yes | Security |
|
|
206
|
+
| Shopping cart | No | Yes | Persistence needed |
|
|
207
|
+
|
|
208
|
+
## Benefits of URL State
|
|
209
|
+
|
|
210
|
+
1. **Shareable**: Users can share exact filtered views
|
|
211
|
+
2. **Bookmarkable**: Save specific searches/filters
|
|
212
|
+
3. **Browser history**: Back/forward works as expected
|
|
213
|
+
4. **SEO**: Search engines can index filtered pages
|
|
214
|
+
5. **Refresh-safe**: State survives page reload
|
|
215
|
+
6. **Deep linking**: Direct links to specific states
|
|
216
|
+
|
|
217
|
+
## Clean URL Best Practices
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
// GOOD: Clean, readable URLs
|
|
221
|
+
/products?category=electronics&sort=price&q=phone
|
|
222
|
+
|
|
223
|
+
// AVOID: Overly complex URLs
|
|
224
|
+
/products?filters=%7B%22category%22%3A%22electronics%22%7D
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
// Use defaults to keep URLs minimal
|
|
229
|
+
const sort = useRouteQuery('sort', 'newest')
|
|
230
|
+
|
|
231
|
+
// URL shows: /products (when using default sort)
|
|
232
|
+
// URL shows: /products?sort=price-low (when changed)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Reference
|
|
236
|
+
- [VueUse - useRouteQuery](https://vueuse.org/router/useRouteQuery/)
|
|
237
|
+
- [Vue Router - Query Parameters](https://router.vuejs.org/guide/essentials/passing-props.html#passing-props-to-route-components)
|
|
238
|
+
- [Mastering Pinia - URL State](https://masteringpinia.com/blog/top-5-mistakes-to-avoid-when-using-pinia)
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Use Pinia for Large-Scale Vue Applications
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Hand-rolled reactive stores lack DevTools integration, TypeScript support, and debugging capabilities needed for production apps
|
|
5
|
+
type: best-practice
|
|
6
|
+
tags: [vue3, pinia, state-management, devtools, architecture, scalability]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Use Pinia for Large-Scale Vue Applications
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - While Vue's Composition API allows creating simple reactive stores with `reactive()` and `ref()`, these hand-rolled solutions lack the tooling, conventions, and debugging capabilities needed for large-scale production applications. Pinia is the official Vue state management solution and should be used for any non-trivial application.
|
|
12
|
+
|
|
13
|
+
## Task Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] Use Pinia for applications with shared state across multiple components
|
|
16
|
+
- [ ] Use Pinia when team collaboration requires consistent patterns
|
|
17
|
+
- [ ] Use Pinia when you need Vue DevTools state debugging
|
|
18
|
+
- [ ] Hand-rolled `reactive()` is acceptable only for simple, single-developer apps
|
|
19
|
+
- [ ] Migrate from Vuex to Pinia (Vuex is in maintenance mode)
|
|
20
|
+
|
|
21
|
+
## When Hand-Rolled State is Acceptable
|
|
22
|
+
|
|
23
|
+
Simple reactive state is fine for:
|
|
24
|
+
- Prototypes and proof-of-concepts
|
|
25
|
+
- Very small applications with minimal shared state
|
|
26
|
+
- Single-developer projects with limited scope
|
|
27
|
+
- Learning purposes
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
// Simple hand-rolled store - OK for small apps
|
|
31
|
+
import { reactive, readonly } from 'vue'
|
|
32
|
+
|
|
33
|
+
export const store = reactive({
|
|
34
|
+
count: 0,
|
|
35
|
+
increment() {
|
|
36
|
+
this.count++
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## When to Use Pinia
|
|
42
|
+
|
|
43
|
+
Use Pinia when you have any of these requirements:
|
|
44
|
+
|
|
45
|
+
### 1. DevTools Integration
|
|
46
|
+
|
|
47
|
+
Pinia provides rich Vue DevTools support:
|
|
48
|
+
- Timeline of state changes
|
|
49
|
+
- State inspection and editing
|
|
50
|
+
- Time-travel debugging
|
|
51
|
+
- Action tracking
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
// Pinia store - fully visible in DevTools
|
|
55
|
+
import { defineStore } from 'pinia'
|
|
56
|
+
|
|
57
|
+
export const useCounterStore = defineStore('counter', {
|
|
58
|
+
state: () => ({ count: 0 }),
|
|
59
|
+
actions: {
|
|
60
|
+
increment() {
|
|
61
|
+
this.count++ // Tracked in DevTools timeline
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. TypeScript Support
|
|
68
|
+
|
|
69
|
+
Pinia has excellent TypeScript inference:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// Full type inference without extra configuration
|
|
73
|
+
import { defineStore } from 'pinia'
|
|
74
|
+
|
|
75
|
+
export const useUserStore = defineStore('user', {
|
|
76
|
+
state: () => ({
|
|
77
|
+
name: '',
|
|
78
|
+
age: 0,
|
|
79
|
+
preferences: {
|
|
80
|
+
theme: 'light' as 'light' | 'dark'
|
|
81
|
+
}
|
|
82
|
+
}),
|
|
83
|
+
|
|
84
|
+
getters: {
|
|
85
|
+
// Return type is inferred
|
|
86
|
+
displayName: (state) => state.name || 'Anonymous'
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
actions: {
|
|
90
|
+
// Full parameter type checking
|
|
91
|
+
setUser(name: string, age: number) {
|
|
92
|
+
this.name = name
|
|
93
|
+
this.age = age
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
// Usage is fully typed
|
|
99
|
+
const userStore = useUserStore()
|
|
100
|
+
userStore.name // string
|
|
101
|
+
userStore.displayName // string
|
|
102
|
+
userStore.setUser('John', 30) // Type-checked
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### 3. Team Collaboration
|
|
106
|
+
|
|
107
|
+
Pinia enforces conventions that help teams:
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
// Consistent structure across all stores
|
|
111
|
+
// stores/products.js
|
|
112
|
+
export const useProductsStore = defineStore('products', {
|
|
113
|
+
state: () => ({ /* ... */ }),
|
|
114
|
+
getters: { /* ... */ },
|
|
115
|
+
actions: { /* ... */ }
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
// stores/cart.js - Same structure
|
|
119
|
+
export const useCartStore = defineStore('cart', {
|
|
120
|
+
state: () => ({ /* ... */ }),
|
|
121
|
+
getters: { /* ... */ },
|
|
122
|
+
actions: { /* ... */ }
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 4. Hot Module Replacement (HMR)
|
|
127
|
+
|
|
128
|
+
Pinia supports HMR out of the box - state persists during development:
|
|
129
|
+
|
|
130
|
+
```javascript
|
|
131
|
+
// State survives code changes during development
|
|
132
|
+
// No need to re-login or re-create state after every edit
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 5. Server-Side Rendering (SSR)
|
|
136
|
+
|
|
137
|
+
Pinia handles SSR state correctly:
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
// Automatic per-request state isolation
|
|
141
|
+
// State serialization for hydration
|
|
142
|
+
// No cross-request pollution
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 6. Plugin Ecosystem
|
|
146
|
+
|
|
147
|
+
Pinia supports plugins for common needs:
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
import { createPinia } from 'pinia'
|
|
151
|
+
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
|
152
|
+
|
|
153
|
+
const pinia = createPinia()
|
|
154
|
+
pinia.use(piniaPluginPersistedstate)
|
|
155
|
+
|
|
156
|
+
// Now stores can persist to localStorage
|
|
157
|
+
export const useSettingsStore = defineStore('settings', {
|
|
158
|
+
state: () => ({
|
|
159
|
+
theme: 'light',
|
|
160
|
+
language: 'en'
|
|
161
|
+
}),
|
|
162
|
+
persist: true // Automatically saved/restored
|
|
163
|
+
})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Pinia vs Hand-Rolled Comparison
|
|
167
|
+
|
|
168
|
+
| Feature | Hand-Rolled `reactive()` | Pinia |
|
|
169
|
+
|---------|-------------------------|-------|
|
|
170
|
+
| DevTools integration | No | Yes |
|
|
171
|
+
| TypeScript inference | Manual | Automatic |
|
|
172
|
+
| HMR support | No | Yes |
|
|
173
|
+
| SSR support | Manual | Built-in |
|
|
174
|
+
| Plugins | No | Yes |
|
|
175
|
+
| Time-travel debugging | No | Yes |
|
|
176
|
+
| Learning curve | Lower | Slightly higher |
|
|
177
|
+
| Bundle size | Smaller | ~1KB |
|
|
178
|
+
| Team conventions | None | Enforced |
|
|
179
|
+
|
|
180
|
+
## Migration from Vuex
|
|
181
|
+
|
|
182
|
+
Vuex is now in maintenance mode. Migrate to Pinia for new features:
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
// Vuex (legacy)
|
|
186
|
+
export default createStore({
|
|
187
|
+
state: { count: 0 },
|
|
188
|
+
mutations: {
|
|
189
|
+
INCREMENT(state) { state.count++ }
|
|
190
|
+
},
|
|
191
|
+
actions: {
|
|
192
|
+
increment({ commit }) { commit('INCREMENT') }
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// Pinia (recommended)
|
|
197
|
+
export const useCounterStore = defineStore('counter', {
|
|
198
|
+
state: () => ({ count: 0 }),
|
|
199
|
+
actions: {
|
|
200
|
+
increment() { this.count++ } // No mutations needed!
|
|
201
|
+
}
|
|
202
|
+
})
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Pinia advantages over Vuex:**
|
|
206
|
+
- No mutations (simpler mental model)
|
|
207
|
+
- Better TypeScript support
|
|
208
|
+
- No nested modules complexity
|
|
209
|
+
- Smaller bundle size
|
|
210
|
+
- Composition API style available
|
|
211
|
+
|
|
212
|
+
## Pinia Store Styles
|
|
213
|
+
|
|
214
|
+
Choose the style that fits your team:
|
|
215
|
+
|
|
216
|
+
### Options Style (Similar to Options API)
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
export const useCounterStore = defineStore('counter', {
|
|
220
|
+
state: () => ({ count: 0 }),
|
|
221
|
+
getters: {
|
|
222
|
+
double: (state) => state.count * 2
|
|
223
|
+
},
|
|
224
|
+
actions: {
|
|
225
|
+
increment() { this.count++ }
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Setup Style (Composition API)
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
export const useCounterStore = defineStore('counter', () => {
|
|
234
|
+
const count = ref(0)
|
|
235
|
+
const double = computed(() => count.value * 2)
|
|
236
|
+
function increment() { count.value++ }
|
|
237
|
+
|
|
238
|
+
return { count, double, increment }
|
|
239
|
+
})
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Quick Start
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
npm install pinia
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
// main.js
|
|
250
|
+
import { createApp } from 'vue'
|
|
251
|
+
import { createPinia } from 'pinia'
|
|
252
|
+
import App from './App.vue'
|
|
253
|
+
|
|
254
|
+
const app = createApp(App)
|
|
255
|
+
app.use(createPinia())
|
|
256
|
+
app.mount('#app')
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Reference
|
|
260
|
+
- [Vue.js - State Management](https://vuejs.org/guide/scaling-up/state-management.html)
|
|
261
|
+
- [Pinia Documentation](https://pinia.vuejs.org/)
|
|
262
|
+
- [Pinia vs Vuex](https://pinia.vuejs.org/introduction.html#comparison-with-vuex)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Include Parentheses When Calling Store Methods in Templates
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: Omitting parentheses on store method calls loses the correct 'this' context, causing unexpected behavior
|
|
5
|
+
type: gotcha
|
|
6
|
+
tags: [vue3, state-management, reactive, methods, templates, this-binding]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Include Parentheses When Calling Store Methods in Templates
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - When calling methods on a reactive store object in Vue templates, you must include parentheses (even without arguments) to ensure the method is called with the correct `this` context. Without parentheses, `this` inside the method may not refer to the store.
|
|
12
|
+
|
|
13
|
+
This is a subtle gotcha that can cause hard-to-debug issues with hand-rolled reactive stores.
|
|
14
|
+
|
|
15
|
+
## Task Checklist
|
|
16
|
+
|
|
17
|
+
- [ ] Always use parentheses when calling store methods in templates: `@click="store.increment()"`
|
|
18
|
+
- [ ] Avoid passing store methods as bare references: `@click="store.increment"` is problematic
|
|
19
|
+
- [ ] Consider using arrow functions or Pinia (which handles this automatically)
|
|
20
|
+
- [ ] Test store method calls work correctly with `this` references
|
|
21
|
+
|
|
22
|
+
## The Problem
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
// store.js
|
|
26
|
+
import { reactive } from 'vue'
|
|
27
|
+
|
|
28
|
+
export const store = reactive({
|
|
29
|
+
count: 0,
|
|
30
|
+
increment() {
|
|
31
|
+
this.count++ // 'this' should refer to the store
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```vue
|
|
37
|
+
<template>
|
|
38
|
+
<!-- WRONG: Without parentheses, 'this' binding may be lost -->
|
|
39
|
+
<button @click="store.increment">
|
|
40
|
+
{{ store.count }}
|
|
41
|
+
</button>
|
|
42
|
+
</template>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
When you write `@click="store.increment"` (without parentheses), Vue passes the method as a callback to the event handler. The method gets called without being bound to the store object, so `this` inside `increment()` may be `undefined` or the global object instead of the store.
|
|
46
|
+
|
|
47
|
+
## The Solution
|
|
48
|
+
|
|
49
|
+
```vue
|
|
50
|
+
<template>
|
|
51
|
+
<!-- CORRECT: With parentheses, method is called with correct context -->
|
|
52
|
+
<button @click="store.increment()">
|
|
53
|
+
{{ store.count }}
|
|
54
|
+
</button>
|
|
55
|
+
</template>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
With parentheses, you're telling Vue to call `store.increment()` directly, which preserves the `this` context as the store object.
|
|
59
|
+
|
|
60
|
+
## Why This Happens
|
|
61
|
+
|
|
62
|
+
In JavaScript, when you reference a method without calling it, you get the function itself without its binding:
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
const store = {
|
|
66
|
+
count: 0,
|
|
67
|
+
increment() {
|
|
68
|
+
this.count++
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// With parentheses - correct context
|
|
73
|
+
store.increment() // this === store ✓
|
|
74
|
+
|
|
75
|
+
// Without parentheses - getting the function
|
|
76
|
+
const fn = store.increment
|
|
77
|
+
fn() // this === undefined (in strict mode) ✗
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Vue's event handler behavior:
|
|
81
|
+
- `@click="store.increment"` - Vue receives `store.increment` as a function and calls it later
|
|
82
|
+
- `@click="store.increment()"` - Vue evaluates `store.increment()` when the event fires
|
|
83
|
+
|
|
84
|
+
## Alternative Solutions
|
|
85
|
+
|
|
86
|
+
### 1. Use Arrow Functions in the Template
|
|
87
|
+
|
|
88
|
+
```vue
|
|
89
|
+
<template>
|
|
90
|
+
<button @click="() => store.increment()">
|
|
91
|
+
{{ store.count }}
|
|
92
|
+
</button>
|
|
93
|
+
</template>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 2. Bind Methods in the Store Definition
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
// store.js
|
|
100
|
+
import { reactive } from 'vue'
|
|
101
|
+
|
|
102
|
+
const state = reactive({
|
|
103
|
+
count: 0
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
// Methods defined separately, arrow functions don't have 'this' issues
|
|
107
|
+
export const store = {
|
|
108
|
+
get count() { return state.count },
|
|
109
|
+
increment: () => { state.count++ }
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 3. Use Pinia (Recommended)
|
|
114
|
+
|
|
115
|
+
Pinia handles method binding correctly:
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
// stores/counter.js
|
|
119
|
+
import { defineStore } from 'pinia'
|
|
120
|
+
|
|
121
|
+
export const useCounterStore = defineStore('counter', {
|
|
122
|
+
state: () => ({ count: 0 }),
|
|
123
|
+
actions: {
|
|
124
|
+
increment() {
|
|
125
|
+
this.count++ // Pinia ensures 'this' is correct
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```vue
|
|
132
|
+
<script setup>
|
|
133
|
+
import { useCounterStore } from '@/stores/counter'
|
|
134
|
+
const counter = useCounterStore()
|
|
135
|
+
</script>
|
|
136
|
+
|
|
137
|
+
<template>
|
|
138
|
+
<!-- Both work correctly with Pinia -->
|
|
139
|
+
<button @click="counter.increment">Works</button>
|
|
140
|
+
<button @click="counter.increment()">Also works</button>
|
|
141
|
+
</template>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 4. Wrap in a Local Function
|
|
145
|
+
|
|
146
|
+
```vue
|
|
147
|
+
<script setup>
|
|
148
|
+
import { store } from './store'
|
|
149
|
+
|
|
150
|
+
// Wrap store method to ensure correct binding
|
|
151
|
+
function increment() {
|
|
152
|
+
store.increment()
|
|
153
|
+
}
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<template>
|
|
157
|
+
<!-- Now safe to use without parentheses -->
|
|
158
|
+
<button @click="increment">
|
|
159
|
+
{{ store.count }}
|
|
160
|
+
</button>
|
|
161
|
+
</template>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## When This Matters
|
|
165
|
+
|
|
166
|
+
This gotcha specifically affects:
|
|
167
|
+
- Hand-rolled reactive stores using `reactive()`
|
|
168
|
+
- Methods that reference `this` inside them
|
|
169
|
+
- Direct method references in templates without parentheses
|
|
170
|
+
|
|
171
|
+
It does NOT affect:
|
|
172
|
+
- Pinia stores (methods are auto-bound)
|
|
173
|
+
- Arrow function methods (no `this` binding)
|
|
174
|
+
- Methods that don't use `this`
|
|
175
|
+
- Method calls with parentheses
|
|
176
|
+
|
|
177
|
+
## Quick Reference
|
|
178
|
+
|
|
179
|
+
| Pattern | Safe? | Notes |
|
|
180
|
+
|---------|-------|-------|
|
|
181
|
+
| `@click="store.method()"` | Yes | Explicit call preserves context |
|
|
182
|
+
| `@click="store.method"` | No* | Context may be lost |
|
|
183
|
+
| `@click="() => store.method()"` | Yes | Arrow wrapper preserves context |
|
|
184
|
+
| `@click="localMethod"` | Yes | Component methods are auto-bound |
|
|
185
|
+
| Pinia: `@click="store.action"` | Yes | Pinia handles binding |
|
|
186
|
+
|
|
187
|
+
*Only problematic if the method uses `this`
|
|
188
|
+
|
|
189
|
+
## Reference
|
|
190
|
+
- [Vue.js State Management - Tip on Method Binding](https://vuejs.org/guide/scaling-up/state-management.html#simple-state-management-with-reactivity-api)
|
|
191
|
+
- [MDN - this in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this)
|
|
@@ -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.
|