前端面试宝典

Vue2 转 Vue3 最容易踩的 10 个坑(高频报错+解决办法)

我直接给你最实用、最常遇到、一踩就报错的坑,每个都告诉你现象 + 为什么错 + 怎么改,看完直接避开 90% 的问题。

1. 响应式相关:直接赋值丢失响应式(最常见!)

<script setup>
import { reactive } from 'vue'
let obj = reactive({ name: '张三' })

// 直接赋值 = 响应式没了!
obj = { name: '李四' } 
</script>

现象:页面不更新,数据变了但视图不变。

原因reactive 创建的对象被整个替换,代理失效。

解决

  • ref 定义
  • 或者修改内部属性,不替换整个对象
// 正确写法1
let obj = ref({ name: '张三' })
obj.value = { name: '李四' }
// 正确写法2
obj.name = '李四'

2. 模板里不用 .value,脚本里必须写 .value

const count = ref(0)
console.log(count) // 打印出来是个对象,不是 0

现象:拿到的值是 RefImpl,不是数字/字符串。

解决

  • <template> 不用写 .value
  • <script setup> 必须写 .value

3. 生命周期函数改名 + 只能在 setup 里用

export default {
  mounted() { ... } // 可以用
}

// 但在 script setup 里不能这么写!

解决

import { onMounted } from 'vue'

setup() {
  onMounted(() => {
    // 只能写在这里
  })
}

改名对照表

  • beforeDestroy → onBeforeUnmount
  • destroyed → onUnmounted

4. 事件总线 on/on / off / $once 被移除了

// Vue2
this.$on('event', () => {})
// Vue3 直接报错:undefined

解决

  • 用第三方库 mitt
  • 或者用 Pinia/Vuex
  • 或者父传子 + 子传父

5. 过滤器 filters 被移除了

<!-- Vue2 -->
<div>{{ msg | filter }}</div>

Vue3 不支持

解决

  • computed 替代
  • 方法调用 替代
const formatMsg = computed(() => msg.value.toUpperCase())

6. this 变了!setup 里没有 this

setup() {
  this.$router // 报错 undefined
}

原因setup 执行时组件还没创建,没有 this

解决

import { useRouter } from 'vue-router'
const router = useRouter()

router.push('/')

7. setset、delete 不需要了,但也用不了了

// Vue2 必须写
this.$set(obj, 'age', 18)

// Vue3 直接写就行
obj.age = 18

原因:Vue3 用 Proxy,天然支持新增/删除属性监听。


8. 根组件创建方式变了

// Vue2
new Vue({ el: '#app' })

// Vue3 报错

解决

import { createApp } from 'vue'
createApp(App).mount('#app')

9. 多根节点(片段)支持,但要注意

Vue2 模板必须只有一个根节点
Vue3 可以多个根节点

<template>
  <div></div>
  <div></div>
</template>

注意:如果组件被父组件用 v-for 绑 key,会有警告。

10. 自定义指令生命周期变了

// Vue2
bind, inserted, update...

// Vue3 全变了
created, beforeMount, mounted, beforeUpdate, updated...

这个坑一般写自定义指令才会遇到。


最最最精简的 5 条保命口诀(背下来)

  1. reactive 不能直接赋值,ref 脚本要写 .value
  2. setup 没有 this,获取 router/store 用 hook
  3. onfilteron、filter、set 全删了
  4. 生命周期变成 onXXX,且只能在 setup 调用
  5. 多根节点允许,但小心样式和 key