Communitygithub.com

vue-patterns

Vue.js 3 Composition API 模式、元件架構、響應式最佳實踐、Pinia 狀態管理、Vue Router 導航及 Nuxt SSR 模式。適用於 Vue、Nuxt、Vite 或 Pinia 專案。在建置或審查 Vue 3、Nuxt 或 Pinia 程式碼時啟用,涵蓋 Composition API、響應式或路由器導航。

vue-patterns 是什麼?

此技能提供一個全面且結構化的指南,專注於使用 Composition API(特別是 `<script setup>` 語法)進行 Vue.js 3 開發。它涵蓋了從專案結構、元件架構 (容器/展示元件)、可複用邏輯 (Composable)、狀態管理 (Pinia Setup Store) 到路由、模板模式、效能優化、測試以及 Nuxt SSR 特有的模式 (如 `useAsyncData`) 等所有面向。該技能也針對 Vue 3.5+ 的新 API (例如 `defineModel()`、`useTemplateRef()`、`useId()`) 提供了詳細介紹,並整理了一份常見的反模式清單及其解決方案。這份技能文件旨在成為開發者在建構或審查 Vue、Nuxt 或 Pinia 專案時的權威參考,透過明確的程式碼範例和實務建議,協助使用者避免常見陷阱並遵循業界最佳實踐。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/vue-patterns

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Vue.js 模式與最佳實踐

使用 Composition API (<script setup>) 進行 Vue.js 3 開發的全面指南,涵蓋元件設計、響應式、狀態管理、路由、測試和 SSR 模式。其中也包含了與一般 Vue 不同的 Nuxt 專用指引。

啟用時機

當以下情況發生時啟用此技能:

  • 專案使用了 Vue.js (任何版本)、Nuxt、Vite + Vue 或 Pinia。
  • 使用者詢問關於 Vue 元件架構、Composable、響應式或狀態管理。
  • 正在審查 Vue 單一檔案元件 (.vue 檔案)。
  • 正在設定 Vue Router、Pinia stores 或 Vite/Vitest 配置。
  • 討論 Vue 特定的效能、安全性或 SSR 模式。

1. 專案結構

推薦佈局 (以功能為優先)

src/
├── api/              # API 客戶端與端點定義
├── assets/           # 靜態資源 (圖片、字體、圖標)
├── components/       # 共用/可複用元件
│   ├── base/         # 基礎 UI 元件 (Button, Input, Modal)
│   └── features/     # 特定功能的共用元件
├── composables/      # 可複用的 Composition API 邏輯
├── layouts/          # 頁面佈局 (可選)
├── pages/            # 路由層級的頁面元件
├── router/           # Vue Router 配置
├── stores/           # Pinia stores
├── types/            # TypeScript 類型定義
├── utils/            # 純工具函數
└── App.vue           # 根元件

檔案命名

慣例使用時機
PascalCase.vue所有元件 (由 vue/multi-word-component-names 強制執行)
useCamelCase.tsComposables
camelCase.ts工具、API 客戶端、類型
kebab-case 目錄路由區段、功能資料夾

2. 元件架構

單一檔案元件順序

<script setup lang="ts">
// 1. 導入 (vue → 生態系 → 絕對路徑 → 相對路徑)
// 2. Props & Emits & Slots
// 3. Composables
// 4. 本地狀態 (ref/reactive)
// 5. 計算屬性
// 6. 方法
// 7. 監聽器
// 8. 生命週期鉤子
</script>

<template>
  <!-- 模板內容 -->
</template>

<style scoped>
  /* 作用域樣式 */
</style>

展示層 vs 容器層元件

  • 容器元件:負責資料擷取、狀態和副作用。渲染展示層元件。
  • 展示層元件:接收 props,觸發事件。不進行 API 呼叫,不存取 store。純渲染。

Props 最佳實踐

// 基於類型的 Props 與預設值
interface Props {
  label: string;
  variant?: "primary" | "secondary";
  disabled?: boolean;
  items: Item[];
}

const props = withDefaults(defineProps<Props>(), {
  variant: "primary",
  disabled: false,
});
  • 始終提供 type,並在適當時提供 required/default。
  • 布林 Props:使用 isXxx、hasXxx、canXxx。
  • 永遠不要直接修改 props — 應改用觸發事件的方式。
  • 對於 v-model 綁定,請使用 defineModel() (Vue 3.4+) 或 modelValue + update:modelValue。

事件

const emit = defineEmits<{
  submit: [];
  "update:modelValue": [value: string];
  select: [id: string, index: number];
}>();
  • 在模板中使用 kebab-case (@update:model-value)。
  • 在 script 中使用 camelCase (emit("update:modelValue", val))。

3. Composables (可複用邏輯)

結構

// composables/useDebounce.ts
export function useDebounce<T>(value: MaybeRef<T>, delay: number): Ref<T> {
  const debounced = ref(toValue(value)) as Ref<T>;

  let timer: ReturnType<typeof setTimeout>;
  watch(
    () => toValue(value),
    (newVal) => {
      clearTimeout(timer);
      timer = setTimeout(() => { debounced.value = newVal; }, delay);
    }
  );

  onUnmounted(() => clearTimeout(timer));
  return readonly(debounced);
}

規則

  • 必須以 use 前綴開頭。
  • 回傳響應式值 (ref、computed、reactive),永遠不要回傳純原始型別。
  • 透過 MaybeRef / toRef() / toValue() 接受響應式輸入。
  • 在 onUnmounted 或監聽器的 onCleanup 中清理副作用。
  • 沒有模組層級的副作用。

vs Mixins

Composables 完全取代了 Vue 2 的 mixins:

  • Mixins:資料流向不透明、來源衝突、命名衝突。
  • Composables:明確的導入、清晰的回傳值、可組合且可進行樹搖最佳化。

4. 狀態管理

何時使用何種模式

模式使用場景
ref() / reactive()本地元件狀態
Props + Emits父子元件通訊
Provide / Inject主題、配置、插件 API
Pinia store全域、共用、複雜狀態
伺服器狀態 Composable帶有快取的 API 資料 (包裝 fetch/TanStack Query)

Pinia Setup Store (偏好方式)

// stores/useCartStore.ts
export const useCartStore = defineStore("cart", () => {
  const items = ref<CartItem[]>([]);
  const isLoading = ref(false);

  const totalPrice = computed(() =>
    items.value.reduce((sum, i) => sum + i.price * i.quantity, 0)
  );
  const itemCount = computed(() =>
    items.value.reduce((sum, i) => sum + i.quantity, 0)
  );

  async function addItem(productId: string) {
    isLoading.value = true;
    try {
      const item = await fetchProduct(productId);
      const existing = items.value.find(i => i.id === item.id);
      if (existing) existing.quantity++;
      else items.value.push({ ...item, quantity: 1 });
    } finally {
      isLoading.value = false;
    }
  }

  return { items, isLoading, totalPrice, itemCount, addItem };
});
  • 使用 Setup Store 語法 (而非 Options Store)。
  • 偏好使用 actions 進行業務層級的變更,並使用 $patch() 進行分組更新。
  • 每個非同步 action:處理 loading + success + error。

5. Vue Router

路由定義

const routes = [
  {
    path: "/users/:id",
    name: "user-detail",
    component: () => import("@/pages/UserDetail.vue"), // 懶加載
    props: true, // 將參數作為 props 傳遞
    meta: { requiresAuth: true },
  },
];

導航守衛

router.beforeEach((to, from) => {
  const { isLoggedIn } = useAuthStore();
  if (to.meta.requiresAuth && !isLoggedIn) {
    return { name: "login", query: { redirect: to.fullPath } };
  }
});

響應式路由參數

當元件保持掛載但路由參數改變時:

const route = useRoute();
const id = computed(() => route.params.id as string);
watch(id, (newId) => fetchItem(newId));

6. 模板模式

模板語法

<!-- v-if/v-else-if/v-else -->
<div v-if="isLoading">載入中...</div>
<div v-else-if="error">錯誤:{{ error }}</div>
<div v-else>{{ content }}</div>

<!-- v-show 用於頻繁切換 -->
<div v-show="isOpen">切換的內容</div>

<!-- 帶有穩定 key 的 v-for -->
<div v-for="item in items" :key="item.id">{{ item.name }}</div>

<!-- 使用計算屬性過濾後的列表 (不在同一元素上使用 v-if + v-for) -->
<div v-for="item in activeItems" :key="item.id">{{ item.name }}</div>

<!-- 事件處理 -->
<form @submit.prevent="handleSubmit">
  <button type="submit">儲存</button>
</form>

<!-- v-model -->
<input v-model="name" />
<CustomInput v-model="value" v-model:title="title" />

7. 效能

技術使用時機
v-memo很少變更的列表項目
v-once只渲染一次且永遠靜態的內容
shallowRef()會被整體替換的大型資料結構
shallowReactive()只有頂層屬性是響應式的
v-show 優於 v-if頻繁的可見性切換
<KeepAlive :max="10">快取被切換的視圖
懶加載路由非關鍵路由使用 () => import(...)
Suspense非同步元件載入,搭配 fallback

8. 測試

技術棧

  • Vitest 用於單元測試和元件測試
  • Vue Test Utils 用於掛載和互動
  • @pinia/testing 用於 store 模擬
  • Playwright 用於端到端測試

元件測試模式

import { mount } from "@vue/test-utils";
import { createPinia, setActivePinia } from "pinia";
import UserCard from "./UserCard.vue";

beforeEach(() => { setActivePinia(createPinia()); });

it("渲染並觸發事件", async () => {
  const wrapper = mount(UserCard, {
    props: { user: { id: "1", name: "Alice" } },
  });
  expect(wrapper.text()).toContain("Alice");
  await wrapper.find("button").trigger("click");
  expect(wrapper.emitted("select")![0]).toEqual(["1"]);
});

9. Nuxt 特定模式

自動導入

Nuxt 自動導入 ref、computed、watch、useFetch、useAsyncData 等。直接使用它們而無需導入。對於非 Nuxt 專案,請始終明確導入。

useAsyncData / useFetch

const { data: user, pending, error, refresh } = await useAsyncData(
  "user", // 用於快取的唯一鍵值
  () => $fetch(`/api/users/${id}`),
);

const { data: posts } = await useFetch("/api/posts", {
  query: { page: 1 },
  key: "posts-page-1", // 用於請求去重
});

伺服器路由

// server/api/users/[id].ts
export default defineEventHandler(async (event) => {
  const { id } = await getValidatedRouterParams(event, z.object({
    id: z.string().uuid(),
  }).parse);
  // ... 擷取並回傳
});

執行環境配置

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // 僅服務端
    apiSecret: "",
    // 公開的 (暴露給客戶端)
    public: {
      apiBase: "https://api.example.com",
    },
  },
});

10. Vue 3.5+ 新 API

響應式 Props 解構

Vue 3.5 穩定了響應式 props 解構 — 從 defineProps() 解構的變數會自動成為響應式:

// Vue 3.5+: 解構的 props 是響應式的 (無需使用 toRefs)
const { count = 0, msg = "hello" } = defineProps<{
  count?: number;
  msg?: string;
}>();

// 限制:無法直接監聽解構後的 prop
watch(() => count, (newVal) => { ... }); // 透過 getter 是必要的

useTemplateRef()

使用 useTemplateRef() 取代名稱匹配的純 ref 來獲取模板引用:

import { useTemplateRef } from "vue";
const inputEl = useTemplateRef<HTMLInputElement>("input");
// "input" 匹配模板中的 ref="input" 屬性,而非變數名稱

支援動態 ref ID:useTemplateRef(dynamicRefId)。

onWatcherCleanup()

全域可導入的監聽器清理 API (Vue 3.5+)。必須在監聽器回調內部同步呼叫:

import { watch, onWatcherCleanup } from "vue";

watch(userId, async (newId) => {
  const controller = new AbortController();
  onWatcherCleanup(() => controller.abort());
  // ... 使用 signal 進行擷取
});

useId()

SSR 穩定的唯一 ID 生成器,用於表單元素和無障礙性:

import { useId } from "vue";
const id = useId();

defer Teleport

<Teleport defer> 允許傳送至在同一個週期內渲染的目標:

<Teleport defer to="#container">內容</Teleport>
<div id="container"></div>

懶加載水合 (SSR)

defineAsyncComponent() 現在支援 hydrate 策略:

import { defineAsyncComponent, hydrateOnVisible } from "vue";
const AsyncComp = defineAsyncComponent({
  loader: () => import("./Comp.vue"),
  hydrate: hydrateOnVisible(),
});

反模式

反模式為什麼是錯的修正方式
解構 defineProps() (Vue < 3.5)取得快照,失去響應性透過 props.xxx 存取或使用 toRefs()
watch() 監聽解構後的 prop (Vue 3.5+)編譯時錯誤 — 解構後的 props 無法直接被監聽使用 getter 包裝:watch(() => count, ...)
在同一元素上使用 v-if + v-for執行順序不明確使用計算屬性過濾後的陣列
v-for 的 key 使用 index重新排序時狀態錯亂使用穩定的資料庫 ID
修改 props違反單向資料流觸發事件或使用 v-model
v-html 搭配使用者內容XSS 漏洞使用 DOMPurify 進行消毒
在 Vue 3 中使用 Mixins不透明、易衝突改用 Composables
Composables 中的模組層級副作用在多個實例間共用範圍限制在 onMounted + onUnmounted 內
對可替換的狀態使用 reactive()替換會破壞響應性改用 ref()
沒有清理的監聽器記憶體洩漏、競爭條件使用 onCleanup 或 onWatcherCleanup() (Vue 3.5+)
在新的 Vue 3 程式碼中使用 Options API生態系已轉向 Composition API使用 <script setup>
對模板引用使用純 ref不支援動態 ref,名稱匹配不穩定改用 useTemplateRef() (Vue 3.5+)

相關技能

  • accessibility — ARIA、語意化 HTML、焦點管理
  • frontend-patterns — 跨框架的前端架構
  • typescript — 應用於 Vue 專案的 TypeScript 最佳實踐
  • coding-standards — 通用程式碼品質標準

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相關技能