Vue2 和 Vue3 的区别

前言

Vue2 和 Vue3 的区别是面试中绕不开的高频考点。本文聚焦最常被问到的核心差异,帮你快速抓住重点。

一、响应式原理(必问)

这是两种方案最本质的差异,也是面试必考题。

Vue2 — Object.defineProperty

1
2
3
4
Object.defineProperty(obj, key, {
get() { /* 依赖收集 */ return val },
set(v) { /* 派发更新 */ val = v }
})

三大缺陷:

  • 无法检测对象属性新增/删除 → 需要 Vue.set() / Vue.delete()
  • 无法监听数组索引和 length → 重写 7 个数组方法(push/pop/shift/unshift/splice/sort/reverse)
  • 初始化递归遍历所有属性,大数据量时性能差

Vue3 — Proxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
new Proxy(target, {
get(target, key, receiver) {
track(target, key)
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
Reflect.set(target, key, value, receiver)
trigger(target, key)
return true
},
deleteProperty(target, key) {
Reflect.deleteProperty(target, key)
trigger(target, key)
return true
}
})

解决了 Vue2 的全部痛点:

  • ✅ 检测属性增删、数组索引和 length 变化
  • ✅ 懒代理 — 按需递归,初始化更快
  • ✅ 支持 Map、Set、WeakMap、WeakSet

面试一句话总结:defineProperty 劫持的是属性,Proxy 劫持的是整个对象。

二、Composition API vs Options API

这也是面试最常问的问题之一。

1
2
3
4
5
6
7
8
9
10
11
12
13
// Vue2 Options API — 按选项拆分,同类逻辑分散
export default {
data() { return { count: 0, name: '' } },
methods { add() { this.count++ }, setName() { ... } },
computed: { double() { return this.count * 2 } },
watch: { count() { ... } }
}

// Vue3 Composition API — 按功能组织,逻辑内聚
import { ref, computed } from 'vue'
const count = ref(0)
const double = computed(() => count.value * 2)
const add = () => count.value++
对比点 Options API Composition API
代码组织 按选项分类,逻辑分散 按功能聚合,高内聚
逻辑复用 Mixins(命名冲突、来源不清晰) Composables(纯函数,来源可追踪)
this 依赖 this,不利于类型推导 无 this,天然支持 TS
Tree-Shaking 不支持 按需引入,打包更小

<script setup> 语法糖是推荐的写法,代码更简洁。

三、生命周期变化

Vue3 映射原名,允许在一个组件中挂载多个钩子。

Vue2 Vue3(Options) Vue3(Composition)
beforeCreate beforeCreate setup() 本身
created created setup() 本身
beforeMount beforeMount onBeforeMount
mounted mounted onMounted
beforeUpdate beforeUpdate onBeforeUpdate
updated updated onUpdated
beforeDestroy beforeUnmount onBeforeUnmount
destroyed unmounted onUnmounted

新增 onRenderTracked、onRenderTriggered 两个调试钩子。

四、v-if / v-for 优先级

这是高频陷阱题。

版本 同级优先级 后果
Vue2 v-for > v-if 先循环再判断,浪费性能
Vue3 v-if > v-for v-if 中访问不到 v-for 的变量,直接报错

无论哪个版本,永远不要同时使用 v-if 和 v-for,用 <template> 包裹或用 computed 过滤。

五、v-model 变化

功能 Vue2 Vue3
单个绑定 v-model="val" v-model="val"
多个绑定 不支持,用 .sync v-model:title v-model:content
.sync 修饰符 :title.sync 移除,合并为 v-model:title
默认 prop/event value / input modelValue / update:modelValue
自定义修饰符 不支持 modelModifiers 可自定义
1
2
3
<!-- Vue3 多 v-model -->
<ChildComponent v-model:title="t" v-model:content="c" />
<!-- 等价于 Vue2 的 :title.sync="t" :content.sync="c" -->

六、已删除的 API

已删除 替代方案
filters 过滤器 computed / 全局方法
$on $off $once mitt / provide + inject
$children $refs
$listeners 合并到 $attrs
Vue.set Vue.delete Proxy 直接操作
Vue.extend defineComponent
new Vue() createApp()

七、性能优化

机制 原理 效果
静态提升 静态节点提升到 render 外,不参与 Diff 减少 VNode 创建
静态标记 编译阶段标记动态节点(PatchFlag) Diff 时跳过静态节点
Block Tree 只记录动态子节点,组成扁平数组 Diff 不递归整棵树
事件缓存 编译时缓存事件函数引用 避免子组件无意义更新
1
2
3
4
5
6
7
8
// 静态提升示意:编译前
<div><span>静态</span><span>{{ msg }}</span></div>

// 编译后
const _hoisted = createVNode('span', null, '静态') // 只创建一次
render() {
return createVNode('div', null, [_hoisted, createVNode('span', null, msg)])
}

八、其他关键差异

差异点 Vue2 Vue3
根节点 必须单个根节点 Fragment 支持多根
TypeScript 类型支持差 源码用 TS 重写
打包体积 ~32KB ~16KB(Tree-Shaking)
Teleport 无 内置 <Teleport>
Suspense 无 内置 <Suspense>
入口 new Vue({ render: h => h(App) }).$mount() createApp(App).mount()

总结

面试高频顺序建议这样记:

  1. 响应式 — defineProperty → Proxy,解决属性增删和数组监听
  2. API 风格 — Options API → Composition API,逻辑复用更优雅
  3. 生命周期 — destroyed → unmounted,setup 替代 beforeCreate + created
  4. v-if / v-for — 优先级反转,永远别同时用
  5. v-model — 支持多个绑定,.sync 被合并
  6. 删除的 API — filters、$on/$off、$children
  7. 性能 — 静态提升 + 静态标记 + Block Tree
  8. 新特性 — Fragment、Teleport、Suspense、Tree-Shaking

新项目直接上 Vue3,老项目用 @vue/compat 渐进式迁移。


内容由 AI 生成,仅供参考。本文发布于 码上学习,转载请注明出处。