# Vue 3 Code Review Guide
> Vue 3 Composition API 代码审查指南,覆盖响应性系统、Props/Emits、Watchers、Composables、Vue 3.5 新特性等核心主题。
## 目录
- [响应性系统](#响应性系统)
- [Props & Emits](#props--emits)
- [Vue 3.5 新特性](#vue-35-新特性)
- [Watchers](#watchers)
- [模板最佳实践](#模板最佳实践)
- [Composables](#composables)
- [性能优化](#性能优化)
- [Review Checklist](#review-checklist)
---
## 响应性系统
### ref vs reactive 选择
```vue
```
### 解构 reactive 对象
```vue
```
### computed 副作用
```vue
```
### shallowRef 优化
```vue
```
---
## Props & Emits
### 直接修改 props
```vue
```
### defineProps 类型声明
```vue
```
### defineEmits 类型安全
```vue
```
---
## Vue 3.5 新特性
### Reactive Props Destructure (3.5+)
```vue
```
### defineModel (3.4+)
```vue
```
### useTemplateRef (3.5+)
```vue
```
### useId (3.5+)
```vue
{{ error }}
```
### onWatcherCleanup (3.5+)
```vue
```
### Deferred Teleport (3.5+)
```vue
```
---
## Watchers
### watch vs watchEffect
```vue
```
### watch 清理函数
```vue
```
### watch 选项
```vue
```
### 监听多个源
```vue
```
---
## 模板最佳实践
### v-for 的 key
```vue
{{ item.name }}
{{ item.name }}
{{ item.name }}
```
### v-if 和 v-for 优先级
```vue
{{ user.name }}
{{ user.name }}
{{ user.name }}
```
### 事件处理
```vue
```
---
## Composables
### Composable 设计原则
```typescript
// ✅ 好的 composable 设计
export function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => count.value++
const decrement = () => count.value--
const reset = () => count.value = initialValue
// 返回响应式引用和方法
return {
count: readonly(count), // 只读防止外部修改
increment,
decrement,
reset
}
}
// ❌ 不要返回 .value
export function useBadCounter() {
const count = ref(0)
return {
count: count.value // ❌ 丢失响应性!
}
}
```
### Props 传递给 composable
```vue
```
### 异步 Composable
```typescript
// ✅ 异步 composable 模式
export function useFetch(url: MaybeRefOrGetter) {
const data = ref(null)
const error = ref(null)
const loading = ref(false)
const execute = async () => {
loading.value = true
error.value = null
try {
const response = await fetch(toValue(url))
if (!response.ok) {
throw new Error(`HTTP ${response.status}`)
}
data.value = await response.json()
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
// 响应式 URL 时自动重新获取
watchEffect(() => {
toValue(url) // 追踪依赖
execute()
})
return {
data: readonly(data),
error: readonly(error),
loading: readonly(loading),
refetch: execute
}
}
// 使用
const { data, loading, error, refetch } = useFetch('/api/users')
```
### 生命周期与清理
```typescript
// ✅ Composable 中正确处理生命周期
export function useEventListener(
target: MaybeRefOrGetter,
event: string,
handler: EventListener
) {
// 组件挂载后添加
onMounted(() => {
toValue(target).addEventListener(event, handler)
})
// 组件卸载时移除
onUnmounted(() => {
toValue(target).removeEventListener(event, handler)
})
}
// ✅ 使用 effectScope 管理副作用
export function useFeature() {
const scope = effectScope()
scope.run(() => {
// 所有响应式效果都在这个 scope 内
const state = ref(0)
watch(state, () => { /* ... */ })
watchEffect(() => { /* ... */ })
})
// 清理所有效果
onUnmounted(() => scope.stop())
return { /* ... */ }
}
```
---
## 性能优化
### v-memo
```vue
```
### defineAsyncComponent
```vue
```
### KeepAlive
```vue
```
### 虚拟列表
```vue
```
---
## Review Checklist
### 响应性系统
- [ ] ref 用于基本类型,reactive 用于对象(或统一用 ref)
- [ ] 没有解构 reactive 对象(或使用了 toRefs)
- [ ] props 传递给 composable 时保持了响应性
- [ ] shallowRef/shallowReactive 用于大型对象优化
- [ ] computed 中没有副作用
### Props & Emits
- [ ] defineProps 使用 TypeScript 类型声明
- [ ] 复杂默认值使用 withDefaults + 工厂函数
- [ ] defineEmits 有完整的类型定义
- [ ] 没有直接修改 props
- [ ] 考虑使用 defineModel 简化 v-model(Vue 3.4+)
### Vue 3.5 新特性(如适用)
- [ ] 使用 Reactive Props Destructure 简化 props 访问
- [ ] 使用 useTemplateRef 替代 ref 属性
- [ ] 表单使用 useId 生成 SSR 安全的 ID
- [ ] 使用 onWatcherCleanup 处理复杂清理逻辑
### Watchers
- [ ] watch/watchEffect 有适当的清理函数
- [ ] 异步 watch 处理了竞态条件
- [ ] flush: 'post' 用于 DOM 操作的 watcher
- [ ] 避免过度使用 watcher(优先用 computed)
- [ ] 考虑 once: true 用于一次性监听
### 模板
- [ ] v-for 使用唯一且稳定的 key
- [ ] v-if 和 v-for 没有在同一元素上
- [ ] 事件处理使用方法而非内联复杂逻辑
- [ ] 大型列表使用虚拟滚动
### Composables
- [ ] 相关逻辑提取到 composables
- [ ] composables 返回响应式引用(不是 .value)
- [ ] 纯函数不要包装成 composable
- [ ] 副作用在组件卸载时清理
- [ ] 使用 effectScope 管理复杂副作用
### 性能
- [ ] 大型组件拆分为小组件
- [ ] 使用 defineAsyncComponent 懒加载
- [ ] 避免不必要的响应式转换
- [ ] v-memo 用于昂贵的列表渲染
- [ ] KeepAlive 用于缓存动态组件