Vue3 组合式 API 常用模式速记(可直接复用)
1) useX:把“可复用状态 + 行为”抽成 composable
最小结构:
ts
// src/composables/useCounter.ts
import { computed, ref } from 'vue'
export function useCounter(initial = 0) {
const count = ref(initial)
const inc = (by = 1) => (count.value += by)
const dec = (by = 1) => (count.value -= by)
const isZero = computed(() => count.value === 0)
return { count, inc, dec, isZero }
}用法:
ts
const { count, inc } = useCounter(10)经验:
useX的返回值要稳定(不要返回一堆临时对象导致解构后失去响应式)- 能返回
ref/computed就别返回原始值
2) “单向数据流”写法:props → 内部 state → emit
当你需要内部可编辑但外部可控时:
ts
const props = defineProps<{ modelValue: string }>()
const emit = defineEmits<{ (e: 'update:modelValue', v: string): void }>()
const inner = computed({
get: () => props.modelValue,
set: (v) => emit('update:modelValue', v)
})模板:
vue
<input v-model="inner" />3) watch 的使用顺序(我自己的习惯)
- 能用
computed就不要watch - 需要“副作用”(请求、写缓存、埋点)才
watch watchEffect适合依赖自动收集,但更容易“跑多次”
例:根据 query 变化触发请求(含取消)
ts
import { ref, watch } from 'vue'
const query = ref('')
const loading = ref(false)
const data = ref<any>(null)
watch(query, async (q, _, onCleanup) => {
if (!q) return
loading.value = true
const ctrl = new AbortController()
onCleanup(() => ctrl.abort())
try {
const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`, {
signal: ctrl.signal
})
data.value = await res.json()
} finally {
loading.value = false
}
})4) 大对象用 shallowRef + 手动触发(性能)
当对象很大、你只关心“整体替换”时:
ts
import { shallowRef } from 'vue'
const big = shallowRef<Record<string, any>>({})
big.value = nextObject // 只追踪引用变化5) 模板里避免重复计算:用 computed 包起来
坏例子(模板里每次渲染都算):
vue
{{ list.filter(x => x.ok).length }}好例子:
ts
const okCount = computed(() => list.value.filter(x => x.ok).length)6) 一个可复用的“加载状态三件套”
ts
const loading = ref(false)
const error = ref<unknown>(null)
const data = ref<T | null>(null)写法上尽量保证:
- 请求开始:
loading=true, error=null - 请求成功:
data=... - 请求失败:
error=... - 最终:
loading=false