Vue3开发避坑指南:10个高频踩坑点与解决方案
今天整理Vue3实际开发中最常见、最容易踩的坑,都是真实业务场景里踩过的雷,看完直接少走弯路。
一、响应式基础:ref/reactive用不对,页面不更新
这是Vue3最经典的坑,几乎每个人第一次写都会遇到。
坑1:直接解构reactive,响应式丢失
const state = reactive({ count: 0 })
// ❌ 错误:解构后变成普通变量,不再响应式
const { count } = state
const add = () => { count++ } // 视图不更新原因:reactive对象解构或展开后,会丢失代理,变成普通值。
正确写法:
// 方案1:用toRefs/toRef保持响应式
const { count } = toRefs(state)
// 方案2:不要解构,直接用state.count
const add = () => { state.count++ }坑2:ref取值忘记加.value
const count = ref(0)
// ❌ 错误
const add = () => { count++ }
// ✅ 正确
const add = () => { count.value++ }小技巧:模板里自动解包,模板中不用写.value,但JS里必须写。
坑3:用reactive包裹基本类型
// ❌ 完全错误,不会生效
const count = reactive(0)规则:
对象/数组 → reactive
字符串/数字/布尔 → ref
不确定一律用ref(最稳)
二、script setup语法糖:API用错导致失效
坑4:defineProps/defineEmits不能放函数里
// ❌ 错误:必须写在顶层,不能包裹在逻辑里
const init = () => {
const props = defineProps({ title: String })
}正确:直接写在最顶层,不需要import。
坑5:父组件拿不到子组件方法(缺少defineExpose)
// 子组件
const openModal = () => { /* ... */ }
// ❌ 父组件ref调用不到
// ✅ 必须暴露
defineExpose({ openModal })三、生命周期与异步:请求时机、清除逻辑踩雷
坑6:onMounted里做DOM操作,但DOM还没渲染完
如果用了v-if或异步数据,直接在onMounted获取DOM很可能是null。
解决方案:用nextTick或用ref绑定元素加watch监听
onMounted(async () => {
await nextTick()
// 现在能拿到DOM
})坑7:定时器/事件监听忘记清除,造成内存泄漏
Vue3组件卸载不会自动清理,必须手动清除:
let timer = null
onMounted(() => {
timer = setInterval(...)
})
onUnmounted(() => {
clearInterval(timer)
})四、侦听器watch:深度监听、立即执行、对象监听
坑8:watch监听reactive整个对象,拿不到新值/旧值
const state = reactive({ count: 0 })
// ❌ 无法正确获取newValue/oldValue
watch(state, (newVal) => { ... })正确:监听具体属性或用函数返回
watch(() => state.count, ...)坑9:开启deep却不做防抖,导致请求疯狂触发
监听表单或对象变化发请求,不加防抖会严重影响性能。建议watch加debounce组合使用。
五、组件通信与v-model:父子数据不同步
坑10:Vue3 v-model变更,用Vue2写法导致不更新
Vue3中:
默认modelValue + update:modelValue
不能直接修改props
// 子组件正确写法
const emit = defineEmits(['update:modelValue'])
const change = (val) => {
emit('update:modelValue', val)
}六、性能与最佳实践
避坑等于提效,记住以下几点:
不要滥用reactive,能用ref就用ref
大数据列表一定要用v-memo或虚拟列表
computed必须只读,不要在computed里修改数据
组件尽量拆小,响应式粒度越细性能越好
接口请求统一封装,统一处理loading、错误、取消请求
七、总结
记住以下Vue3核心避坑点:
| 场景 | 错误做法 | 正确做法 |
|---|---|---|
| 解构reactive | const { count } = state | 用toRefs或直接用state.count |
| ref取值 | count++ | count.value++ |
| 基本类型响应式 | reactive(0) | ref(0) |
| script setup暴露方法 | 直接写方法 | 用defineExpose暴露 |
| watch reactive对象 | watch(state, ...) | 用getter形式() => state.count |
| DOM操作时机 | 直接onMounted里获取 | 配合nextTick |
| 定时器/监听 | 只用onMounted注册 | 在onUnmounted中清除 |
这篇都是真实项目高频问题,非常适合新手查漏补缺,也适合老手当速查手册。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!