[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71PA3RSCF3DBJ3M3XH1D5RV":3,"skill-same:01M71PA3RSCF3DBJ3M3XH1D5RV":85},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":15,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":57,"commitSha":58,"skillDirectory":59,"forksCount":60,"repositoryHtmlUrl":61,"repositoryHomepageUrl":62,"ownerHtmlUrl":63,"ownerBlogUrl":64,"ownerDescription":6,"updatedAt":65,"install":66,"resources":79},"01M71PA3RSCF3DBJ3M3XH1D5RV","vue-patterns",null,"Use when building or reviewing Vue 3, Nuxt or Pinia code and want idioms: Composition API, project layout, reactivity, Vue Router and SSR. A pattern catalogue; for full builds including Quasar mobile and PWA see vue-expert.","monoes\u002Fmonomind","monoes","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F276744696?v=4","01M71KWF37AJ2NJBP3KWBVSDGT","01M71KWF4CSF8ADH0M6HA09VD2",23,"MIT","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fvue-patterns\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fvue-patterns\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"patterns","vue","vue patterns","coding","full","pattern","mobile","ssr","layout","pinia","router","api","code","query","when","frontend","pwa","expert","project","Composition","2026-11-10 13:02:02",false,"---\nname: vue-patterns\ndescription: \"Use when building or reviewing Vue 3, Nuxt or Pinia code and want idioms: Composition API, project layout, reactivity, Vue Router and SSR. A pattern catalogue; for full builds including Quasar mobile and PWA see vue-expert.\"\ntags: [\"frontend\",\"vue\",\"coding-standards\"]\ntools: [\"monograph_query\",\"monograph_context\"]\nlicense: MIT\nsource: https:\u002F\u002Fgithub.com\u002Faffaan-m\u002FECC\nsource_path: \"skills\u002Fvue-patterns\"\nsource_commit: bf70150eb2df8070024e5bdf08e4aa08959e2735\n---\n# Vue.js Patterns and Best Practices\n\nComprehensive guide for Vue.js 3 development using Composition API (`\u003Cscript setup>`), covering component design, reactivity, state management, routing, testing, and SSR patterns. Nuxt-specific guidance is included where it differs from vanilla Vue.\n\n## When to Activate\n\nActivate this skill when:\n- The project uses Vue.js (any version), Nuxt, Vite + Vue, or Pinia.\n- The user asks about Vue component architecture, composables, reactivity, or state management.\n- Reviewing Vue Single-File Components (`.vue` files).\n- Setting up Vue Router, Pinia stores, or Vite\u002FVitest configuration.\n- Discussing Vue-specific performance, security, or SSR patterns.\n\n---\n\n## 1. Project Structure\n\n### Recommended Layout (Feature-First)\n\n```\nsrc\u002F\n├── api\u002F              # API client and endpoint definitions\n├── assets\u002F           # Static assets (images, fonts, icons)\n├── components\u002F       # Shared\u002Freusable components\n│   ├── base\u002F         # Base UI primitives (Button, Input, Modal)\n│   └── features\u002F     # Feature-specific shared components\n├── composables\u002F      # Reusable Composition API logic\n├── layouts\u002F          # Page layouts (optional)\n├── pages\u002F            # Route-level page components\n├── router\u002F           # Vue Router configuration\n├── stores\u002F           # Pinia stores\n├── types\u002F            # TypeScript type definitions\n├── utils\u002F            # Pure utility functions\n└── App.vue           # Root component\n```\n\n### File Naming\n\n| Convention | When to Use |\n|-----------|-------------|\n| `PascalCase.vue` | All components (enforced by `vue\u002Fmulti-word-component-names`) |\n| `useCamelCase.ts` | Composables |\n| `camelCase.ts` | Utilities, API clients, types |\n| `kebab-case` directories | Route segments, feature folders |\n\n---\n\n## 2. Component Architecture\n\n### Single-File Component Order\n\n```vue\n\u003Cscript setup lang=\"ts\">\n\u002F\u002F 1. Imports (vue → ecosystem → absolute → relative)\n\u002F\u002F 2. Props & Emits & Slots\n\u002F\u002F 3. Composables\n\u002F\u002F 4. Local state (ref\u002Freactive)\n\u002F\u002F 5. Computed properties\n\u002F\u002F 6. Methods\n\u002F\u002F 7. Watchers\n\u002F\u002F 8. Lifecycle hooks\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- Template content -->\n\u003C\u002Ftemplate>\n\n\u003Cstyle scoped>\n  \u002F* Scoped styles *\u002F\n\u003C\u002Fstyle>\n```\n\n### Presentational vs Container\n\n- **Container components**: Own data fetching, state, and side effects. Render presentational components.\n- **Presentational components**: Receive props, emit events. No API calls, no store access. Pure rendering.\n\n### Props Best Practices\n\n```ts\n\u002F\u002F Type-based props with defaults\ninterface Props {\n  label: string;\n  variant?: \"primary\" | \"secondary\";\n  disabled?: boolean;\n  items: Item[];\n}\n\nconst props = withDefaults(defineProps\u003CProps>(), {\n  variant: \"primary\",\n  disabled: false,\n});\n```\n\n- Always provide `type`, and `required`\u002F`default` where appropriate.\n- Boolean props: `isXxx`, `hasXxx`, `canXxx`.\n- Never mutate props — emit events instead.\n- For v-model binding, use `defineModel()` (Vue 3.4+) or `modelValue` + `update:modelValue`.\n\n### Events\n\n```ts\nconst emit = defineEmits\u003C{\n  submit: [];\n  \"update:modelValue\": [value: string];\n  select: [id: string, index: number];\n}>();\n```\n\n- Use kebab-case in templates (`@update:model-value`).\n- Use camelCase in script (`emit(\"update:modelValue\", val)`).\n\n---\n\n## 3. Composables (Reusable Logic)\n\n### Structure\n\n```ts\n\u002F\u002F composables\u002FuseDebounce.ts\nexport function useDebounce\u003CT>(value: MaybeRef\u003CT>, delay: number): Ref\u003CT> {\n  const debounced = ref(toValue(value)) as Ref\u003CT>;\n\n  let timer: ReturnType\u003Ctypeof setTimeout>;\n  watch(\n    () => toValue(value),\n    (newVal) => {\n      clearTimeout(timer);\n      timer = setTimeout(() => { debounced.value = newVal; }, delay);\n    }\n  );\n\n  onUnmounted(() => clearTimeout(timer));\n  return readonly(debounced);\n}\n```\n\n### Rules\n\n- Must start with `use` prefix.\n- Return reactive values (`ref`, `computed`, `reactive`), never plain primitives.\n- Accept reactive inputs via `MaybeRef` \u002F `toRef()` \u002F `toValue()`.\n- Clean up side effects in `onUnmounted` or watcher `onCleanup`.\n- No module-scope side effects.\n\n### vs Mixins\n\nComposables replace Vue 2 mixins entirely:\n- **Mixins**: Opaque data flow, source-of-truth collisions, name conflicts.\n- **Composables**: Explicit imports, clear return values, composable and tree-shakable.\n\n---\n\n## 4. State Management\n\n### When to Use What\n\n| Pattern | Use Case |\n|---------|----------|\n| `ref()` \u002F `reactive()` | Local component state |\n| Props + Emits | Parent-child communication |\n| Provide \u002F Inject | Theme, config, plugin API |\n| Pinia store | Global, shared, complex state |\n| Server state composable | API data with caching (wrap `fetch`\u002FTanStack Query) |\n\n### Pinia Setup Store (Preferred)\n\n```ts\n\u002F\u002F stores\u002FuseCartStore.ts\nexport const useCartStore = defineStore(\"cart\", () => {\n  const items = ref\u003CCartItem[]>([]);\n  const isLoading = ref(false);\n\n  const totalPrice = computed(() =>\n    items.value.reduce((sum, i) => sum + i.price * i.quantity, 0)\n  );\n  const itemCount = computed(() =>\n    items.value.reduce((sum, i) => sum + i.quantity, 0)\n  );\n\n  async function addItem(productId: string) {\n    isLoading.value = true;\n    try {\n      const item = await fetchProduct(productId);\n      const existing = items.value.find(i => i.id === item.id);\n      if (existing) existing.quantity++;\n      else items.value.push({ ...item, quantity: 1 });\n    } finally {\n      isLoading.value = false;\n    }\n  }\n\n  return { items, isLoading, totalPrice, itemCount, addItem };\n});\n```\n\n- Use Setup Store syntax (not Options Store).\n- Prefer actions for business-level mutations and `$patch()` for grouped updates.\n- Every async action: handle loading + success + error.\n\n---\n\n## 5. Vue Router\n\n### Route Definitions\n\n```ts\nconst routes = [\n  {\n    path: \"\u002Fusers\u002F:id\",\n    name: \"user-detail\",\n    component: () => import(\"@\u002Fpages\u002FUserDetail.vue\"), \u002F\u002F lazy\n    props: true, \u002F\u002F pass params as props\n    meta: { requiresAuth: true },\n  },\n];\n```\n\n### Navigation Guards\n\n```ts\nrouter.beforeEach((to, from) => {\n  const { isLoggedIn } = useAuthStore();\n  if (to.meta.requiresAuth && !isLoggedIn) {\n    return { name: \"login\", query: { redirect: to.fullPath } };\n  }\n});\n```\n\n### Reactive Route Params\n\nWhen a component stays mounted but route params change:\n\n```ts\nconst route = useRoute();\nconst id = computed(() => route.params.id as string);\nwatch(id, (newId) => fetchItem(newId));\n```\n\n---\n\n## 6. Template Patterns\n\n### Template Syntax\n\n```vue\n\u003C!-- v-if\u002Fv-else-if\u002Fv-else -->\n\u003Cdiv v-if=\"isLoading\">Loading...\u003C\u002Fdiv>\n\u003Cdiv v-else-if=\"error\">Error: {{ error }}\u003C\u002Fdiv>\n\u003Cdiv v-else>{{ content }}\u003C\u002Fdiv>\n\n\u003C!-- v-show for frequent toggles -->\n\u003Cdiv v-show=\"isOpen\">Toggled content\u003C\u002Fdiv>\n\n\u003C!-- v-for with stable keys -->\n\u003Cdiv v-for=\"item in items\" :key=\"item.id\">{{ item.name }}\u003C\u002Fdiv>\n\n\u003C!-- Computed filtered list (not v-if + v-for on same element) -->\n\u003Cdiv v-for=\"item in activeItems\" :key=\"item.id\">{{ item.name }}\u003C\u002Fdiv>\n\n\u003C!-- Event handling -->\n\u003Cform @submit.prevent=\"handleSubmit\">\n  \u003Cbutton type=\"submit\">Save\u003C\u002Fbutton>\n\u003C\u002Fform>\n\n\u003C!-- v-model -->\n\u003Cinput v-model=\"name\" \u002F>\n\u003CCustomInput v-model=\"value\" v-model:title=\"title\" \u002F>\n```\n\n---\n\n## 7. Performance\n\n| Technique | When to Use |\n|-----------|-------------|\n| `v-memo` | List items that rarely change |\n| `v-once` | Content rendered once and static forever |\n| `shallowRef()` | Large data structures replaced wholesale |\n| `shallowReactive()` | Only top-level properties are reactive |\n| `v-show` over `v-if` | Frequent visibility toggles |\n| `\u003CKeepAlive :max=\"10\">` | Cache toggled views |\n| Lazy routes | `() => import(...)` for non-critical routes |\n| `Suspense` | Async component loading with fallback |\n\n---\n\n## 8. Testing\n\n### Stack\n\n- **Vitest** for unit and component tests\n- **Vue Test Utils** for mounting and interaction\n- **@pinia\u002Ftesting** for store mocking\n- **Playwright** for E2E\n\n### Component Test Pattern\n\n```ts\nimport { mount } from \"@vue\u002Ftest-utils\";\nimport { createPinia, setActivePinia } from \"pinia\";\nimport UserCard from \".\u002FUserCard.vue\";\n\nbeforeEach(() => { setActivePinia(createPinia()); });\n\nit(\"renders and emits\", async () => {\n  const wrapper = mount(UserCard, {\n    props: { user: { id: \"1\", name: \"Alice\" } },\n  });\n  expect(wrapper.text()).toContain(\"Alice\");\n  await wrapper.find(\"button\").trigger(\"click\");\n  expect(wrapper.emitted(\"select\")![0]).toEqual([\"1\"]);\n});\n```\n\n---\n\n## 9. Nuxt-Specific Patterns\n\n### Auto-Imports\n\nNuxt auto-imports `ref`, `computed`, `watch`, `useFetch`, `useAsyncData`, etc. Use them directly without importing. For non-Nuxt projects, always import explicitly.\n\n### useAsyncData \u002F useFetch\n\n```ts\nconst { data: user, pending, error, refresh } = await useAsyncData(\n  \"user\", \u002F\u002F unique key for caching\n  () => $fetch(`\u002Fapi\u002Fusers\u002F${id}`),\n);\n\nconst { data: posts } = await useFetch(\"\u002Fapi\u002Fposts\", {\n  query: { page: 1 },\n  key: \"posts-page-1\", \u002F\u002F dedupes requests\n});\n```\n\n### Server Routes\n\n```ts\n\u002F\u002F server\u002Fapi\u002Fusers\u002F[id].ts\nexport default defineEventHandler(async (event) => {\n  const { id } = await getValidatedRouterParams(event, z.object({\n    id: z.string().uuid(),\n  }).parse);\n  \u002F\u002F ... fetch and return\n});\n```\n\n### Runtime Config\n\n```ts\n\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  runtimeConfig: {\n    \u002F\u002F server-only\n    apiSecret: \"\",\n    \u002F\u002F public (exposed to client)\n    public: {\n      apiBase: \"https:\u002F\u002Fapi.example.com\",\n    },\n  },\n});\n```\n\n---\n\n## 10. Vue 3.5+ New APIs\n\n### Reactive Props Destructure\n\nVue 3.5 stabilized reactive props destructure — destructured variables from `defineProps()` are automatically reactive:\n\n```ts\n\u002F\u002F Vue 3.5+: destructured props are reactive (no need for toRefs)\nconst { count = 0, msg = \"hello\" } = defineProps\u003C{\n  count?: number;\n  msg?: string;\n}>();\n\n\u002F\u002F Limitation: cannot watch destructured prop directly\nwatch(() => count, (newVal) => { ... }); \u002F\u002F PASS getter required\n```\n\n### `useTemplateRef()`\n\nReplace name-matched plain refs with `useTemplateRef()` for template references:\n\n```ts\nimport { useTemplateRef } from \"vue\";\nconst inputEl = useTemplateRef\u003CHTMLInputElement>(\"input\");\n\u002F\u002F \"input\" matches the ref=\"input\" attribute in template, not the variable name\n```\n\nSupports dynamic ref IDs: `useTemplateRef(dynamicRefId)`.\n\n### `onWatcherCleanup()`\n\nGlobally importable watcher cleanup API (Vue 3.5+). It must be called synchronously inside the watcher callback:\n\n```ts\nimport { watch, onWatcherCleanup } from \"vue\";\n\nwatch(userId, async (newId) => {\n  const controller = new AbortController();\n  onWatcherCleanup(() => controller.abort());\n  \u002F\u002F ... fetch with signal\n});\n```\n\n### `useId()`\n\nSSR-stable unique ID generation for form elements and accessibility:\n\n```ts\nimport { useId } from \"vue\";\nconst id = useId();\n```\n\n### `defer` Teleport\n\n`\u003CTeleport defer>` allows teleporting to targets rendered in the same cycle:\n\n```vue\n\u003CTeleport defer to=\"#container\">Content\u003C\u002FTeleport>\n\u003Cdiv id=\"container\">\u003C\u002Fdiv>\n```\n\n### Lazy Hydration (SSR)\n\n`defineAsyncComponent()` now supports `hydrate` strategy:\n\n```ts\nimport { defineAsyncComponent, hydrateOnVisible } from \"vue\";\nconst AsyncComp = defineAsyncComponent({\n  loader: () => import(\".\u002FComp.vue\"),\n  hydrate: hydrateOnVisible(),\n});\n```\n\n---\n\n## Anti-Patterns\n\n| Anti-Pattern | Why It's Wrong | The Fix |\n|-------------|---------------|---------|\n| Destructuring `defineProps()` (Vue \u003C 3.5) | Captures snapshot, loses reactivity | Access via `props.xxx` or use `toRefs()` |\n| `watch()` on destructured prop (Vue 3.5+) | Compile-time error — destructured props can't be watched directly | Use getter wrapper: `watch(() => count, ...)` |\n| `v-if` + `v-for` on same element | Ambiguous execution order | Use computed filtered array |\n| `v-for` key = index | Broken state on reorder | Use stable database IDs |\n| Mutating props | Violates one-way data flow | Emit events or use `v-model` |\n| `v-html` with user content | XSS vulnerability | Sanitize with DOMPurify |\n| Mixins in Vue 3 | Opaque, collision-prone | Replace with composables |\n| Module-scope side effects in composable | Shared across instances | Scope in `onMounted` + `onUnmounted` |\n| `reactive()` for replaceable state | Replacement breaks reactivity | Use `ref()` instead |\n| Watcher without cleanup | Memory leaks, race conditions | Use `onCleanup` or `onWatcherCleanup()` (Vue 3.5+) |\n| Options API in new Vue 3 code | Ecosystem move to Composition API | Use `\u003Cscript setup>` |\n| Plain ref for template references | No dynamic ref support, name-matching fragile | Use `useTemplateRef()` (Vue 3.5+) |\n\n## Related Skills\n\n- `accessibility` — ARIA, semantic HTML, focus management\n- `frontend-patterns` — Cross-framework frontend architecture\n- `typescript` — TypeScript best practices applied to Vue projects\n- `coding-standards` — General code quality standards\n","# Vue.js Patterns and Best Practices\n\nComprehensive guide for Vue.js 3 development using Composition API (`\u003Cscript setup>`), covering component design, reactivity, state management, routing, testing, and SSR patterns. Nuxt-specific guidance is included where it differs from vanilla Vue.\n\n## When to Activate\n\nActivate this skill when:\n- The project uses Vue.js (any version), Nuxt, Vite + Vue, or Pinia.\n- The user asks about Vue component architecture, composables, reactivity, or state management.\n- Reviewing Vue Single-File Components (`.vue` files).\n- Setting up Vue Router, Pinia stores, or Vite\u002FVitest configuration.\n- Discussing Vue-specific performance, security, or SSR patterns.\n\n---\n\n## 1. Project Structure\n\n### Recommended Layout (Feature-First)\n\n```\nsrc\u002F\n├── api\u002F              # API client and endpoint definitions\n├── assets\u002F           # Static assets (images, fonts, icons)\n├── components\u002F       # Shared\u002Freusable components\n│   ├── base\u002F         # Base UI primitives (Button, Input, Modal)\n│   └── features\u002F     # Feature-specific shared components\n├── composables\u002F      # Reusable Composition API logic\n├── layouts\u002F          # Page layouts (optional)\n├── pages\u002F            # Route-level page components\n├── router\u002F           # Vue Router configuration\n├── stores\u002F           # Pinia stores\n├── types\u002F            # TypeScript type definitions\n├── utils\u002F            # Pure utility functions\n└── App.vue           # Root component\n```\n\n### File Naming\n\n| Convention | When to Use |\n|-----------|-------------|\n| `PascalCase.vue` | All components (enforced by `vue\u002Fmulti-word-component-names`) |\n| `useCamelCase.ts` | Composables |\n| `camelCase.ts` | Utilities, API clients, types |\n| `kebab-case` directories | Route segments, feature folders |\n\n---\n\n## 2. Component Architecture\n\n### Single-File Component Order\n\n```vue\n\u003Cscript setup lang=\"ts\">\n\u002F\u002F 1. Imports (vue → ecosystem → absolute → relative)\n\u002F\u002F 2. Props & Emits & Slots\n\u002F\u002F 3. Composables\n\u002F\u002F 4. Local state (ref\u002Freactive)\n\u002F\u002F 5. Computed properties\n\u002F\u002F 6. Methods\n\u002F\u002F 7. Watchers\n\u002F\u002F 8. Lifecycle hooks\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- Template content -->\n\u003C\u002Ftemplate>\n\n\u003Cstyle scoped>\n  \u002F* Scoped styles *\u002F\n\u003C\u002Fstyle>\n```\n\n### Presentational vs Container\n\n- **Container components**: Own data fetching, state, and side effects. Render presentational components.\n- **Presentational components**: Receive props, emit events. No API calls, no store access. Pure rendering.\n\n### Props Best Practices\n\n```ts\n\u002F\u002F Type-based props with defaults\ninterface Props {\n  label: string;\n  variant?: \"primary\" | \"secondary\";\n  disabled?: boolean;\n  items: Item[];\n}\n\nconst props = withDefaults(defineProps\u003CProps>(), {\n  variant: \"primary\",\n  disabled: false,\n});\n```\n\n- Always provide `type`, and `required`\u002F`default` where appropriate.\n- Boolean props: `isXxx`, `hasXxx`, `canXxx`.\n- Never mutate props — emit events instead.\n- For v-model binding, use `defineModel()` (Vue 3.4+) or `modelValue` + `update:modelValue`.\n\n### Events\n\n```ts\nconst emit = defineEmits\u003C{\n  submit: [];\n  \"update:modelValue\": [value: string];\n  select: [id: string, index: number];\n}>();\n```\n\n- Use kebab-case in templates (`@update:model-value`).\n- Use camelCase in script (`emit(\"update:modelValue\", val)`).\n\n---\n\n## 3. Composables (Reusable Logic)\n\n### Structure\n\n```ts\n\u002F\u002F composables\u002FuseDebounce.ts\nexport function useDebounce\u003CT>(value: MaybeRef\u003CT>, delay: number): Ref\u003CT> {\n  const debounced = ref(toValue(value)) as Ref\u003CT>;\n\n  let timer: ReturnType\u003Ctypeof setTimeout>;\n  watch(\n    () => toValue(value),\n    (newVal) => {\n      clearTimeout(timer);\n      timer = setTimeout(() => { debounced.value = newVal; }, delay);\n    }\n  );\n\n  onUnmounted(() => clearTimeout(timer));\n  return readonly(debounced);\n}\n```\n\n### Rules\n\n- Must start with `use` prefix.\n- Return reactive values (`ref`, `computed`, `reactive`), never plain primitives.\n- Accept reactive inputs via `MaybeRef` \u002F `toRef()` \u002F `toValue()`.\n- Clean up side effects in `onUnmounted` or watcher `onCleanup`.\n- No module-scope side effects.\n\n### vs Mixins\n\nComposables replace Vue 2 mixins entirely:\n- **Mixins**: Opaque data flow, source-of-truth collisions, name conflicts.\n- **Composables**: Explicit imports, clear return values, composable and tree-shakable.\n\n---\n\n## 4. State Management\n\n### When to Use What\n\n| Pattern | Use Case |\n|---------|----------|\n| `ref()` \u002F `reactive()` | Local component state |\n| Props + Emits | Parent-child communication |\n| Provide \u002F Inject | Theme, config, plugin API |\n| Pinia store | Global, shared, complex state |\n| Server state composable | API data with caching (wrap `fetch`\u002FTanStack Query) |\n\n### Pinia Setup Store (Preferred)\n\n```ts\n\u002F\u002F stores\u002FuseCartStore.ts\nexport const useCartStore = defineStore(\"cart\", () => {\n  const items = ref\u003CCartItem[]>([]);\n  const isLoading = ref(false);\n\n  const totalPrice = computed(() =>\n    items.value.reduce((sum, i) => sum + i.price * i.quantity, 0)\n  );\n  const itemCount = computed(() =>\n    items.value.reduce((sum, i) => sum + i.quantity, 0)\n  );\n\n  async function addItem(productId: string) {\n    isLoading.value = true;\n    try {\n      const item = await fetchProduct(productId);\n      const existing = items.value.find(i => i.id === item.id);\n      if (existing) existing.quantity++;\n      else items.value.push({ ...item, quantity: 1 });\n    } finally {\n      isLoading.value = false;\n    }\n  }\n\n  return { items, isLoading, totalPrice, itemCount, addItem };\n});\n```\n\n- Use Setup Store syntax (not Options Store).\n- Prefer actions for business-level mutations and `$patch()` for grouped updates.\n- Every async action: handle loading + success + error.\n\n---\n\n## 5. Vue Router\n\n### Route Definitions\n\n```ts\nconst routes = [\n  {\n    path: \"\u002Fusers\u002F:id\",\n    name: \"user-detail\",\n    component: () => import(\"@\u002Fpages\u002FUserDetail.vue\"), \u002F\u002F lazy\n    props: true, \u002F\u002F pass params as props\n    meta: { requiresAuth: true },\n  },\n];\n```\n\n### Navigation Guards\n\n```ts\nrouter.beforeEach((to, from) => {\n  const { isLoggedIn } = useAuthStore();\n  if (to.meta.requiresAuth && !isLoggedIn) {\n    return { name: \"login\", query: { redirect: to.fullPath } };\n  }\n});\n```\n\n### Reactive Route Params\n\nWhen a component stays mounted but route params change:\n\n```ts\nconst route = useRoute();\nconst id = computed(() => route.params.id as string);\nwatch(id, (newId) => fetchItem(newId));\n```\n\n---\n\n## 6. Template Patterns\n\n### Template Syntax\n\n```vue\n\u003C!-- v-if\u002Fv-else-if\u002Fv-else -->\n\u003Cdiv v-if=\"isLoading\">Loading...\u003C\u002Fdiv>\n\u003Cdiv v-else-if=\"error\">Error: {{ error }}\u003C\u002Fdiv>\n\u003Cdiv v-else>{{ content }}\u003C\u002Fdiv>\n\n\u003C!-- v-show for frequent toggles -->\n\u003Cdiv v-show=\"isOpen\">Toggled content\u003C\u002Fdiv>\n\n\u003C!-- v-for with stable keys -->\n\u003Cdiv v-for=\"item in items\" :key=\"item.id\">{{ item.name }}\u003C\u002Fdiv>\n\n\u003C!-- Computed filtered list (not v-if + v-for on same element) -->\n\u003Cdiv v-for=\"item in activeItems\" :key=\"item.id\">{{ item.name }}\u003C\u002Fdiv>\n\n\u003C!-- Event handling -->\n\u003Cform @submit.prevent=\"handleSubmit\">\n  \u003Cbutton type=\"submit\">Save\u003C\u002Fbutton>\n\u003C\u002Fform>\n\n\u003C!-- v-model -->\n\u003Cinput v-model=\"name\" \u002F>\n\u003CCustomInput v-model=\"value\" v-model:title=\"title\" \u002F>\n```\n\n---\n\n## 7. Performance\n\n| Technique | When to Use |\n|-----------|-------------|\n| `v-memo` | List items that rarely change |\n| `v-once` | Content rendered once and static forever |\n| `shallowRef()` | Large data structures replaced wholesale |\n| `shallowReactive()` | Only top-level properties are reactive |\n| `v-show` over `v-if` | Frequent visibility toggles |\n| `\u003CKeepAlive :max=\"10\">` | Cache toggled views |\n| Lazy routes | `() => import(...)` for non-critical routes |\n| `Suspense` | Async component loading with fallback |\n\n---\n\n## 8. Testing\n\n### Stack\n\n- **Vitest** for unit and component tests\n- **Vue Test Utils** for mounting and interaction\n- **@pinia\u002Ftesting** for store mocking\n- **Playwright** for E2E\n\n### Component Test Pattern\n\n```ts\nimport { mount } from \"@vue\u002Ftest-utils\";\nimport { createPinia, setActivePinia } from \"pinia\";\nimport UserCard from \".\u002FUserCard.vue\";\n\nbeforeEach(() => { setActivePinia(createPinia()); });\n\nit(\"renders and emits\", async () => {\n  const wrapper = mount(UserCard, {\n    props: { user: { id: \"1\", name: \"Alice\" } },\n  });\n  expect(wrapper.text()).toContain(\"Alice\");\n  await wrapper.find(\"button\").trigger(\"click\");\n  expect(wrapper.emitted(\"select\")![0]).toEqual([\"1\"]);\n});\n```\n\n---\n\n## 9. Nuxt-Specific Patterns\n\n### Auto-Imports\n\nNuxt auto-imports `ref`, `computed`, `watch`, `useFetch`, `useAsyncData`, etc. Use them directly without importing. For non-Nuxt projects, always import explicitly.\n\n### useAsyncData \u002F useFetch\n\n```ts\nconst { data: user, pending, error, refresh } = await useAsyncData(\n  \"user\", \u002F\u002F unique key for caching\n  () => $fetch(`\u002Fapi\u002Fusers\u002F${id}`),\n);\n\nconst { data: posts } = await useFetch(\"\u002Fapi\u002Fposts\", {\n  query: { page: 1 },\n  key: \"posts-page-1\", \u002F\u002F dedupes requests\n});\n```\n\n### Server Routes\n\n```ts\n\u002F\u002F server\u002Fapi\u002Fusers\u002F[id].ts\nexport default defineEventHandler(async (event) => {\n  const { id } = await getValidatedRouterParams(event, z.object({\n    id: z.string().uuid(),\n  }).parse);\n  \u002F\u002F ... fetch and return\n});\n```\n\n### Runtime Config\n\n```ts\n\u002F\u002F nuxt.config.ts\nexport default defineNuxtConfig({\n  runtimeConfig: {\n    \u002F\u002F server-only\n    apiSecret: \"\",\n    \u002F\u002F public (exposed to client)\n    public: {\n      apiBase: \"https:\u002F\u002Fapi.example.com\",\n    },\n  },\n});\n```\n\n---\n\n## 10. Vue 3.5+ New APIs\n\n### Reactive Props Destructure\n\nVue 3.5 stabilized reactive props destructure — destructured variables from `defineProps()` are automatically reactive:\n\n```ts\n\u002F\u002F Vue 3.5+: destructured props are reactive (no need for toRefs)\nconst { count = 0, msg = \"hello\" } = defineProps\u003C{\n  count?: number;\n  msg?: string;\n}>();\n\n\u002F\u002F Limitation: cannot watch destructured prop directly\nwatch(() => count, (newVal) => { ... }); \u002F\u002F PASS getter required\n```\n\n### `useTemplateRef()`\n\nReplace name-matched plain refs with `useTemplateRef()` for template references:\n\n```ts\nimport { useTemplateRef } from \"vue\";\nconst inputEl = useTemplateRef\u003CHTMLInputElement>(\"input\");\n\u002F\u002F \"input\" matches the ref=\"input\" attribute in template, not the variable name\n```\n\nSupports dynamic ref IDs: `useTemplateRef(dynamicRefId)`.\n\n### `onWatcherCleanup()`\n\nGlobally importable watcher cleanup API (Vue 3.5+). It must be called synchronously inside the watcher callback:\n\n```ts\nimport { watch, onWatcherCleanup } from \"vue\";\n\nwatch(userId, async (newId) => {\n  const controller = new AbortController();\n  onWatcherCleanup(() => controller.abort());\n  \u002F\u002F ... fetch with signal\n});\n```\n\n### `useId()`\n\nSSR-stable unique ID generation for form elements and accessibility:\n\n```ts\nimport { useId } from \"vue\";\nconst id = useId();\n```\n\n### `defer` Teleport\n\n`\u003CTeleport defer>` allows teleporting to targets rendered in the same cycle:\n\n```vue\n\u003CTeleport defer to=\"#container\">Content\u003C\u002FTeleport>\n\u003Cdiv id=\"container\">\u003C\u002Fdiv>\n```\n\n### Lazy Hydration (SSR)\n\n`defineAsyncComponent()` now supports `hydrate` strategy:\n\n```ts\nimport { defineAsyncComponent, hydrateOnVisible } from \"vue\";\nconst AsyncComp = defineAsyncComponent({\n  loader: () => import(\".\u002FComp.vue\"),\n  hydrate: hydrateOnVisible(),\n});\n```\n\n---\n\n## Anti-Patterns\n\n| Anti-Pattern | Why It's Wrong | The Fix |\n|-------------|---------------|---------|\n| Destructuring `defineProps()` (Vue \u003C 3.5) | Captures snapshot, loses reactivity | Access via `props.xxx` or use `toRefs()` |\n| `watch()` on destructured prop (Vue 3.5+) | Compile-time error — destructured props can't be watched directly | Use getter wrapper: `watch(() => count, ...)` |\n| `v-if` + `v-for` on same element | Ambiguous execution order | Use computed filtered array |\n| `v-for` key = index | Broken state on reorder | Use stable database IDs |\n| Mutating props | Violates one-way data flow | Emit events or use `v-model` |\n| `v-html` with user content | XSS vulnerability | Sanitize with DOMPurify |\n| Mixins in Vue 3 | Opaque, collision-prone | Replace with composables |\n| Module-scope side effects in composable | Shared across instances | Scope in `onMounted` + `onUnmounted` |\n| `reactive()` for replaceable state | Replacement breaks reactivity | Use `ref()` instead |\n| Watcher without cleanup | Memory leaks, race conditions | Use `onCleanup` or `onWatcherCleanup()` (Vue 3.5+) |\n| Options API in new Vue 3 code | Ecosystem move to Composition API | Use `\u003Cscript setup>` |\n| Plain ref for template references | No dynamic ref support, name-matching fragile | Use `useTemplateRef()` (Vue 3.5+) |\n\n## Related Skills\n\n- `accessibility` — ARIA, semantic HTML, focus management\n- `frontend-patterns` — Cross-framework frontend architecture\n- `typescript` — TypeScript best practices applied to Vue projects\n- `coding-standards` — General code quality standards\n",{"name":5,"tags":49,"tools":51,"source":54,"license":14,"description":7,"source_path":55,"source_commit":56},[39,25,50],"coding-standards",[52,53],"monograph_query","monograph_context","https:\u002F\u002Fgithub.com\u002Faffaan-m\u002FECC","skills\u002Fvue-patterns","bf70150eb2df8070024e5bdf08e4aa08959e2735","main","f7cd4595d0f8959822163e14f9787117765c931a","packages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fvue-patterns",2,"https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind","https:\u002F\u002Fmonoes.github.io\u002Fmonomind\u002F","https:\u002F\u002Fgithub.com\u002Fmonoes","https:\u002F\u002Fmonoes.me","2026-11-10 01:53:48",{"source":8,"skillName":5,"commands":67,"prompt":77,"zipAvailable":78},[68,71,74],{"tool":69,"command":70},"npx","npx skills add monoes\u002Fmonomind --skill vue-patterns",{"tool":72,"command":73},"bunx","bunx skills add monoes\u002Fmonomind --skill vue-patterns",{"tool":75,"command":76},"pnpm","pnpm dlx skills add monoes\u002Fmonomind --skill vue-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fvue-patterns\u002FSKILL.md",true,[80],{"path":81,"resourceType":82,"resourceTypeDesc":83,"sizeBytes":84,"sourceHtmlUrl":15},"LICENSE.txt","LICENSE","许可证",1071,[86,113,144,171,196,223],{"skillNo":87,"name":88,"nameZh":6,"description":89,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":90,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":93,"publishedAt":44},"01M71PB3SHWKMNJM6MD65VX8D6","zk-steward","Org role guidance for a ZK steward: engineer zero-knowledge proof systems with circuit design, SNARK vs STARK choice and security review. This is zero-knowledge cryptography, not Zettelkasten note-taking.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fzk-steward\u002FSKILL.md",[],[94,95,96,97,98,99,100,101,102,37,103,104,105,106,107,108,109,110,111,112],"zk","zk steward","steward","zero-knowledge","engineer","choice","design","knowledge","impact","snark","review","monomind","security review","stark","zettelkasten","cryptography","security","org","blockchain",{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":117,"sourceHtmlUrl":118,"categories":119,"keywords":128,"publishedAt":44},"01M71PB18SDG28WBKPKJNB814K","writing-plans","Use when you have a spec or requirements for a multi-step coding task and must write an implementation plan before touching code. Produces bite-sized TDD tasks with exact file paths, code and commands for an engineer with zero context.","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwriting-plans\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwriting-plans\u002FSKILL.md",[120,123],{"categoryNo":121,"categoryName":122,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":124,"categoryName":125,"taxonomyType":126,"taxonomyTypeDesc":127},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[129,130,131,98,132,133,38,134,37,36,135,136,137,138,139,140,141,27,142,143],"writing","writing plans","plans","superpowers","implementation plan","you","tasks","plan","product","write","commands","paths","suggest","context","ZeRO",{"skillNo":145,"name":146,"nameZh":6,"description":147,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":90,"licenseFileUrl":148,"sourceHtmlUrl":149,"categories":150,"keywords":154,"publishedAt":44},"01M71PAXFM488X9ARXK0T04TF5","write-spec","Write a feature spec or PRD from a problem statement or feature idea. Use when turning a vague idea or user request into a structured document, scoping a feature with goals and non-goals, defining success metrics and acceptance criteria, or breaking a big ask into a phased spec.","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwrite-spec\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwrite-spec\u002FSKILL.md",[151],{"categoryNo":152,"categoryName":153,"taxonomyType":126,"taxonomyTypeDesc":127},"01JBSEEDCAT000000000000031","产品经理",[138,155,156,157,158,101,159,38,160,161,162,137,163,164,165,166,167,168,169,170],"write spec","spec","user request","problem","acceptance","ask","vague","work","request","scoping","idea","metrics","feature","problem statement","use","document",{"skillNo":172,"name":173,"nameZh":6,"description":174,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":90,"licenseFileUrl":175,"sourceHtmlUrl":176,"categories":177,"keywords":181,"publishedAt":44},"01M71PASSZY800C0RPEARBS918","write-query","Write optimized SQL for your dialect with best practices. Use when translating a natural-language data need into SQL, building a multi-CTE query with joins and aggregations, optimizing a query against a large partitioned table, or getting dialect-specific syntax for Snowflake, BigQuery, Postgres, etc.","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwrite-query\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fwrite-query\u002FSKILL.md",[178],{"categoryNo":179,"categoryName":180,"taxonomyType":126,"taxonomyTypeDesc":127},"01JBSEEDCAT000000000000033","数据分析师",[37,138,182,183,184,185,101,169,38,186,187,188,189,162,190,191,192,193,194,195],"write query","postgres","for","snowflake","best practices","natural-language","with","multi","github","sql","https","syntax","plugins","data",{"skillNo":197,"name":198,"nameZh":6,"description":199,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":90,"licenseFileUrl":6,"sourceHtmlUrl":200,"categories":201,"keywords":208,"publishedAt":44},"01M71PAPTX0T3PF6ETFVR96SJJ","workflow-automation","Org role guidance for workflow automation: build and maintain CI\u002FCD definitions in GitHub Actions or equivalent: triggers, jobs, matrices, caching. Covers path filters, dependency caching, parallel jobs, timeouts and fail-fast ordering.","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fworkflow-automation\u002FSKILL.md",[202,205],{"categoryNo":203,"categoryName":204,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000011","运维与部署",{"categoryNo":206,"categoryName":207,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[209,210,211,212,184,213,214,215,216,102,217,218,219,37,105,190,220,221,222,192],"Workflow Automation","automation","workflow","parallel","actions","ci","triggers","github actions","maintain","filters","caching","build","dependency","devops",{"skillNo":224,"name":225,"nameZh":6,"description":226,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":90,"licenseFileUrl":6,"sourceHtmlUrl":227,"categories":228,"keywords":230,"publishedAt":44},"01M71PAM19Z1XTV9NPCFSE2MY8","workflow-architect","Org role guidance for a workflow architect: map full workflow trees for systems, user journeys and agents, covering every branch and failure. Covers discovering workflows from code, handoff contracts, timeouts, recovery paths and observable state.","https:\u002F\u002Fgithub.com\u002Fmonoes\u002Fmonomind\u002Fblob\u002Fmain\u002Fpackages\u002F@monomind\u002Fcli\u002Forg-skills\u002Fworkflow-architect\u002FSKILL.md",[229],{"categoryNo":206,"categoryName":207,"taxonomyType":21,"taxonomyTypeDesc":22},[211,231,232,233,234,235,236,184,237,238,36,239,240,241,105,190,242,243,192,244],"workflow architect","architect","trees","user journeys","branch","planning","map","workflows","architecture","state","leadership","recovery","operations","systems"]