Skip to main content Skills Marketplace Découvrez et explorez les compétences IA créées par la communauté.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Copier le promptAfficher les détails du prompt Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
npx skills add https://github.com/aaione/everything-claude-code-zh --skill vue-patternsLa commande reste sur une seule ligne. Faites défiler horizontalement pour la vérifier avant de la copier.
Vous préférez une copie locale ? Téléchargez les fichiers actuellement disponibles dans SkillsMP.
Télécharger Zip Téléchargement... Métiers associés SOC
Basé sur la classification professionnelle SOC
name vue-patterns description Vue.js 3 Composition API 模式、组件架构、响应式最佳实践、Pinia 状态管理、Vue Router 导航和 Nuxt SSR 模式。在 Vue、Nuxt、Vite 或 Pinia 项目中激活。 origin ECC
Vue.js 模式与最佳实践
使用 Composition API(<script setup>)进行 Vue.js 3 开发的综合指南,覆盖组件设计、响应式、状态管理、路由、测试和 SSR 模式。Nuxt 专属指引在与原生 Vue 不同处给出。
何时激活
在以下情况激活:
项目使用 Vue.js(任何版本)、Nuxt、Vite + Vue 或 Pinia。
用户问 Vue 组件架构、composables、响应式或状态管理。
审查 Vue Single-File Components(.vue 文件)。
搭建 Vue Router、Pinia stores 或 Vite/Vitest 配置。
讨论 Vue 专属性能、安全或 SSR 模式。
1. 项目结构
推荐布局(Feature-First)
src/
├── api/ # API client 和 endpoint 定义
├── assets/ # 静态资源(图片、字体、图标)
├── components/ # 共享/复用组件
│ ├── base/ # 基础 UI primitives(Button、Input、Modal)
│ └── features/ # Feature 专属共享组件
├── composables/ # 可复用 Composition API 逻辑
├── layouts/ # 页面布局(可选)
├── pages/ # Route 级页面组件
├── router/ # Vue Router 配置
├── stores/ # Pinia stores
├── types/ # TypeScript 类型定义
├── utils/ # 纯工具函数
└── App.vue # 根组件
文件命名
约定 何时使用 PascalCase.vue所有组件(由 vue/multi-word-component-names 强制) useCamelCase.tsComposables camelCase.ts工具、API clients、types 目录
kebab-case
Route segments、feature 文件夹
2. 组件架构
Single-File Component 顺序 <script setup lang="ts">
// 1. Imports(vue → ecosystem → 绝对 → 相对)
// 2. Props & Emits & Slots
// 3. Composables
// 4. 本地状态(ref/reactive)
// 5. Computed 属性
// 6. Methods
// 7. Watchers
// 8. 生命周期 hooks
</script>
<template>
<!-- Template 内容 -->
</template>
<style scoped>
/* Scoped 样式 */
</style>
Presentational vs Container
Container 组件 :拥有数据获取、状态和副作用。渲染 presentational 组件。
Presentational 组件 :接收 props、emit 事件。无 API 调用、无 store 访问。纯渲染。
Props 最佳实践
interface Props {
label : string ;
variant ?: "primary" | "secondary" ;
disabled ?: boolean ;
items : Item [];
}
const props = withDefaults (defineProps<Props >(), {
variant : "primary" ,
disabled : false ,
});
始终提供 type,并在合适时提供 required/default。
Boolean props:isXxx、hasXxx、canXxx。
绝不修改 props——改为 emit 事件。
对 v-model 绑定,使用 defineModel()(Vue 3.4+)或 modelValue + update:modelValue。
事件 const emit = defineEmits<{
submit : [];
"update:modelValue" : [value : string ];
select : [id : string , index : number ];
}>();
在 templates 中用 kebab-case(@update:model-value)。
在 script 中用 camelCase(emit("update:modelValue", val))。
3. Composables(可复用逻辑)
结构
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),绝不返回普通 primitives。
通过 MaybeRef / toRef() / toValue() 接受响应式输入。
在 onUnmounted 或 watcher onCleanup 中清理副作用。
无模块作用域副作用。
vs Mixins Composables 完全取代 Vue 2 mixins:
Mixins :不透明数据流、source-of-truth 冲突、命名冲突。
Composables :显式 imports、清晰返回值、可组合且 tree-shakable。
4. 状态管理
何时用什么 模式 用例 ref() / reactive()本地组件状态 Props + Emits 父子通信 Provide / Inject Theme、config、plugin API Pinia store 全局、共享、复杂状态 Server state composable 带 cache 的 API 数据(包装 fetch/TanStack Query)
Pinia Setup Store(首选)
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)。
业务级 mutation 优先用 actions,成组更新用 $patch()。
每个异步 action:处理 loading + success + error。
5. Vue Router
Route 定义 const routes = [
{
path : "/users/:id" ,
name : "user-detail" ,
component : () => import ("@/pages/UserDetail.vue" ),
props : true ,
meta : { requiresAuth : true },
},
];
Navigation Guards router.beforeEach ((to, from ) => {
const { isLoggedIn } = useAuthStore ();
if (to.meta .requiresAuth && !isLoggedIn) {
return { name : "login" , query : { redirect : to.fullPath } };
}
});
响应式 Route Params 当组件保持挂载但 route params 变化时:
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">Loading...</div>
<div v-else-if="error">Error: {{ error }}</div>
<div v-else>{{ content }}</div>
<!-- v-show 用于频繁切换 -->
<div v-show="isOpen">Toggled content</div>
<!-- v-for 带稳定 key -->
<div v-for="item in items" :key="item.id">{{ item.name }}</div>
<!-- Computed 过滤列表(不是同一元素上的 v-if + v-for) -->
<div v-for="item in activeItems" :key="item.id">{{ item.name }}</div>
<!-- 事件处理 -->
<form @submit.prevent="handleSubmit">
<button type="submit">Save</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 over v-if频繁的可见性切换 <KeepAlive :max="10">缓存切换的视图 懒加载 routes () => import(...) 用于非关键 routesSuspense带 fallback 的异步组件加载
8. 测试
技术栈
Vitest 用于单元和组件测试
Vue Test Utils 用于挂载和交互
@pinia/testing 用于 store mocking
Playwright 用于 E2E
组件测试模式 import { mount } from "@vue/test-utils" ;
import { createPinia, setActivePinia } from "pinia" ;
import UserCard from "./UserCard.vue" ;
beforeEach (() => { setActivePinia (createPinia ()); });
it ("renders and emits" , 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 专属模式
Auto-Imports Nuxt 自动导入 ref、computed、watch、useFetch、useAsyncData 等。直接使用,无需 import。非 Nuxt 项目始终显式 import。
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 Routes
export default defineEventHandler (async (event) => {
const { id } = await getValidatedRouterParams (event, z.object ({
id : z.string ().uuid (),
}).parse );
});
Runtime Config
export default defineNuxtConfig ({
runtimeConfig : {
apiSecret : "" ,
public : {
apiBase : "https://api.example.com" ,
},
},
});
10. Vue 3.5+ 新 API
Reactive Props Destructure Vue 3.5 稳定了 reactive props destructure——从 defineProps() 解构的变量会自动保持响应式:
const { count = 0 , msg = "hello" } = defineProps<{
count ?: number ;
msg ?: string ;
}>();
watch (() => count, (newVal ) => { ... });
useTemplateRef()用 useTemplateRef() 替代名称匹配的普通 ref 来做 template references:
import { useTemplateRef } from "vue" ;
const inputEl = useTemplateRef<HTMLInputElement >("input" );
支持动态 ref IDs:useTemplateRef(dynamicRefId)。
onWatcherCleanup()全局可 import 的 watcher cleanup API(Vue 3.5+)。必须在 watcher callback 内同步调用:
import { watch, onWatcherCleanup } from "vue" ;
watch (userId, async (newId) => {
const controller = new AbortController ();
onWatcherCleanup (() => controller.abort ());
});
useId()SSR 稳定的唯一 ID 生成,用于表单元素和可访问性:
import { useId } from "vue" ;
const id = useId ();
defer Teleport<Teleport defer> 允许 teleport 到同一周期内渲染的目标:
<Teleport defer to="#container">Content</Teleport>
<div id="container"></div>
懒加载 Hydration(SSR) defineAsyncComponent() 现支持 hydrate 策略:
import { defineAsyncComponent, hydrateOnVisible } from "vue" ;
const AsyncComp = defineAsyncComponent ({
loader : () => import ("./Comp.vue" ),
hydrate : hydrateOnVisible (),
});
反模式 反模式 为什么错 修复 解构 defineProps()(Vue < 3.5) 捕获快照,丢失响应式 通过 props.xxx 访问或用 toRefs() 对解构 prop watch()(Vue 3.5+) 编译期错误——解构 props 不能直接 watch 用 getter 包装:watch(() => count, ...) 同一元素上 v-if + v-for 执行顺序歧义 用 computed 过滤数组 v-for key = index重排时状态错乱 用稳定数据库 IDs 修改 props 违反单向数据流 emit 事件或用 v-model 用用户内容 v-html XSS 漏洞 用 DOMPurify 净化 Vue 3 中用 Mixins 不透明、易冲突 替换为 composables Composable 中模块作用域副作用 跨实例共享 在 onMounted + onUnmounted 中限定 reactive() 用于可替换状态替换会破坏响应式 改用 ref() 无清理的 watcher 内存泄漏、竞态 用 onCleanup 或 onWatcherCleanup()(Vue 3.5+) 新 Vue 3 代码用 Options API 生态转向 Composition API 用 <script setup> 用普通 ref 做 template references 不支持动态 ref、名称匹配脆弱 用 useTemplateRef()(Vue 3.5+)
相关技能
accessibility — ARIA、语义 HTML、focus 管理
frontend-patterns — 跨框架前端架构
typescript — 应用于 Vue 项目的 TypeScript 最佳实践
coding-standards — 通用代码质量标准