Vue 3 编程的 10 个实用技巧
(2025–2026 仍在大量实际项目中高频使用的核心写法)
以下内容聚焦 Vue 3 + Composition API 的真实工程实践,基本都是“写过很多项目后觉得最值得反复使用的技巧”。
1. 善用 toRefs 解构 reactive 对象,保持响应式
最容易犯的错误之一:解构 reactive 后丢失响应式。
// ❌ 错误写法
const state = reactive({ count: 0, name: 'tom' })
const { count, name } = state // count 和 name 不再是响应式的
// ✅ 正确写法
const state = reactive({ count: 0, name: 'tom' })
const { count, name } = toRefs(state)
// 或者一次性全部解构
const { count, name, ...rest } = toRefs(state)
小技巧:当你需要把 reactive 对象传给子组件 props 时,也推荐用 toRefs 解构后传递单个 ref。
2. 使用 shallowRef / shallowReactive 做性能优化
当对象/数组非常大且你只关心顶层变化时,关闭深层响应能显著提升性能。
// 大型配置对象,只关心整体替换,不关心内部属性变化
const config = shallowRef({ theme: 'dark', size: 'large', ...很多字段 })
// 只需要这样替换就会触发更新
config.value = newConfig
// 或者 shallowReactive(对象顶层属性响应,嵌套不响应)
const form = shallowReactive({ name: '', age: 0, address: { city: '' } })
form.name = 'new' // 触发
form.address.city = 'xx' // 不会触发
适用场景:大型列表数据、表单初始值、第三方库返回的大对象。
3. 善用 computed 的 getter + setter 双向绑定
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (newValue: string) => {
const [first, ...rest] = newValue.trim().split(' ')
firstName.value = first || ''
lastName.value = rest.join(' ') || ''
}
})
// 使用时就像 v-model
<input v-model="fullName" />
这在复杂表单、地址拆分、金额千分位格式化等场景非常实用。
4. 自定义 ref + 自动转换(最优雅的输入处理方式)
function useTrimmedRef(initial = '') {
return customRef<string>((track, trigger) => {
let value = initial
return {
get() {
track()
return value
},
set(newValue) {
const trimmed = newValue.trim()
if (trimmed !== value) {
value = trimmed
trigger()
}
}
}
})
}
const username = useTrimmedRef('')
类似还可以做:数字自动转 number、自动大写、自动补零、输入防抖等。
5. 组合式函数(Composables)最佳实践命名与结构
推荐的 composable 文件结构与命名:
// src/composables/useUserInfo.ts
export function useUserInfo(userId?: string) {
const user = ref(null)
const loading = ref(false)
const error = ref(null)
const fetchUser = async () => {
// ...
}
onMounted(fetchUser)
return {
user,
loading,
error,
refresh: fetchUser
}
}
命名约定(社区最常见):
- useXxx → 最常见
- useXxxState → 有状态管理
- useXxxFetch → 数据请求相关
- useXxxForm → 表单相关
- useXxxModal → 弹窗控制
6. 用 watch + immediate + deep 的正确姿势
// 推荐写法(清晰、可读性高)
watch(
() => props.someObject,
(newVal, oldVal) => {
// 处理逻辑
},
{ deep: true, immediate: true }
)
// 更推荐:当你只关心某个属性时
watch(
() => props.user?.id,
(newId, oldId) => {
if (newId) fetchUserDetail(newId)
},
{ immediate: true }
)
7. 用 markRaw 和 unref 解决第三方库和 ref 嵌套问题
// 第三方库实例不要被代理(会很慢或出错)
const chartInstance = markRaw(new Chart(ctx, config))
// 快速取值(避免 .value 写太多)
const count = unref(someRef) // ref 或 reactive 都适用
8. 优雅处理异步请求 + loading + error 状态
function useAsyncFn<T>(fn: () => Promise<T>) {
const loading = ref(false)
const error = ref<Error | null>(null)
const data = ref<T | null>(null)
const execute = async (...args: any[]) => {
loading.value = true
error.value = null
try {
data.value = await fn(...args)
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
return { execute, loading, error, data }
}
9. v-model 组件的现代写法(defineModel)
Vue 3.3+ 推荐写法(极大简化双向绑定组件)
// 子组件 MyInput.vue
const modelValue = defineModel<string>({ required: true })
// 等价于旧写法:
const modelValue = defineProps<{ modelValue: string }>().modelValue
const emit = defineEmits(['update:modelValue'])
甚至可以指定默认值、转换器:
const count = defineModel<number>({
default: 0,
get: (val) => Number(val),
set: (val) => String(val)
})
10. 使用 useTemplateRef + onMounted 简化 DOM 操作(Vue 3.5+)
<template>
<input ref="inputEl" />
</template>
<script setup>
const inputEl = useTemplateRef<HTMLInputElement>('inputEl')
onMounted(() => {
inputEl.value?.focus()
})
</script>
不再需要 ref(null) 再类型断言,更加安全。
额外加餐:最常被忽略但非常好用的两个小技巧
- useSlots() 判断插槽是否存在
const hasHeader = !!useSlots().header - useAttrs() 透传所有未声明的属性
非常适合封装组件时把 class、style、data-* 等自动透传给根元素。
const attrs = useAttrs()
这 10 个技巧覆盖了 Vue 3 日常开发中 80% 以上最容易踩坑和最能提升代码质量的地方。
你目前用 Vue 3 开发中最头疼的是哪一块?
(比如组合式函数组织、类型定义、性能优化、组件封装、状态管理……)
可以告诉我,我可以针对性再展开更详细的实战写法。