
使用方式
关于
使用 Composition API、服务端渲染和模块开发构建可扩展的 Vue 3 和 Nuxt 3 应用。
name: vue-ops description: "Vue 3 开发模式、Composition API、Pinia 状态管理、Vue Router 和 Nuxt 3。适用于:vue、vuejs、composition api、pinia、vue router、nuxt、nuxt3、script setup、composable、reactive、defineProps、defineEmits、defineModel、v-model、provide inject、vue3。" license: MIT allowed-tools: "Read Write Bash" metadata: author: claude-mods related-skills: typescript-ops, testing-ops, tailwind-ops, javascript-ops
Vue 操作指南
全面的 Vue 3 参考,涵盖 Composition API、Pinia、Vue Router、Nuxt 3 和测试——全程使用 TypeScript 的生产模式。
响应式决策树
我需要让什么数据变成响应式的?
│
├─ 单个原始值(字符串、数字、布尔值)?
│ └─ ref()
│ const count = ref(0)
│ const name = ref('')
│
├─ 需要深层响应式的普通对象或数组?
│ ├─ 是否需要解构或单独传递属性?
│ │ └─ reactive() — 解构时使用 toRefs()
│ └─ 是否需要整体替换对象?
│ └─ ref() — ref.value = newObject
│
├─ 从其他响应式源派生/计算的状态?
│ └─ computed()
│ const doubled = computed(() => count.value * 2)
│
├─ 只有顶层键变化的大对象?
│ └─ shallowRef() 或 shallowReactive()
│ const state = shallowRef({ nested: { big: 'data' } })
│
├─ 依赖变化时应运行的副作用?
│ ├─ 不需要旧值,自动追踪依赖?
│ │ └─ watchEffect(() => { ... })
│ └─ 需要新旧值、显式源或延迟执行?
│ └─ watch(source, (newVal, oldVal) => { ... })
│
└─ 不应该是响应式的数据(原始 DOM、第三方实例)?
└─ markRaw(obj) 或 shallowRef(obj)
组件通信决策树
数据需要传递多远?
│
├─ 父组件 → 直接子组件?
│ └─ props (defineProps)
│ 直接、显式、类型安全
│
├─ 子组件 → 父组件(用户操作/数据更新)?
│ └─ emit (defineEmits)
│ defineEmits<{ change: [value: string] }>()
│
├─ 父子组件双向绑定?
│ └─ 通过 defineModel() 实现 v-model(Vue 3.4+)
│ const model = defineModel<string>()
│
├─ 祖先 → 深层后代(props 逐层传递问题)?
│ └─ provide / inject
│ 使用 InjectionKey<T> 确保类型安全
│
├─ 兄弟组件或无关组件?
│ ├─ 简单/少量共享值?
│ │ └─ 从共同祖先 provide / inject
│ └─ 复杂共享状态或跨树通信?
│ └─ Pinia store
│
├─ 真正的全局状态(用户会话、购物车、偏好设置)?
│ └─ Pinia store
│ 使用 setup 语法的 defineStore
│
└─ 远距离组件间的一次性事件(少见)?
└─ Pinia action + watch,或 mitt 事件总线
避免:Vue 3 已移除根实例上的 $emit
Composition API 快速参考
<script setup> — 标准写法
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'
// Props — 使用 TypeScript 泛型(无需运行时声明)
const props = defineProps<{
title: string
count?: number
}>()
// 带默认值的 Props
const props = withDefaults(defineProps<{
size: 'sm' | 'md' | 'lg'
disabled?: boolean
}>(), {
size: 'md',
disabled: false,
})
// Emits — 类型安全的事件签名
const emit = defineEmits<{
change: [value: string] // 命名元组语法(Vue 3.3+)
update: [id: number, data: object]
close: []
}>()
// 响应式状态
const count = ref(0)
const user = reactive({ name: '', email: '' })
// 计算属性
const doubled = computed(() => count.value * 2)
// 侦听器
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
// 生命周期
onMounted(() => {
console.log('component mounted')
})
</script>
defineModel — v-model 绑定(Vue 3.4+)
<!-- 子组件:MyInput.vue -->
<script setup lang="ts">
const model = defineModel<string>({ required: true })
// 命名 v-model:<MyInput v-model:title="..." />
const title = defineModel<string>('title')
// 带修饰符
const [modelValue, modifiers] = defineModel<string, 'trim' | 'uppercase'>()
</script>
<template>
<input :value="model" @input="model = $event.target.value" />
</template>
defineExpose — 暴露给父组件 ref
<script setup lang="ts">
const inputRef = ref<HTMLInputElement | null>(null)
function focus() {
inputRef.value?.focus()
}
// 暴露公共 API 供父组件模板 ref 使用
defineExpose({ focus })
</script>
defineOptions — 组件元信息(Vue 3.3+)
<script setup lang="ts">
defineOptions({
name: 'MyComponent',
inheritAttrs: false,
})
</script>
defineSlots — 类型化插槽(Vue 3.3+)
<script setup lang="ts">
defineSlots<{
default(props: { item: User }): any
header(props: {}): any
}>()
</script>
Pinia 快速入门
Setup 语法(推荐——组合式风格)
// stores/counter.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubled, increment }
})
兼容工具
Claude CodeCursorGitHub Copilot
标签
前端开发
