前端面试中,最常见的问题是:"vue2和vue3有那些区别?"
今天,从这个问题开始,逐步梳理Web前端知识点。
区别1:响应式原理不同
Vue2 用 defineProperty,Vue3 用 Proxy
区别2:代码编写不同
Vue3 用 setup(),Vue2 用 data/methods
区别3:根节点数量不同
Vue2只能由1个根节点,Vue3可以有多个根节点
区别4:生命周期不同
Vue3合并了beforeUnmounted和beforeDestroy,合并了Unmounted和destory。
区别5:编译时 + 虚拟 DOM 的运行时优化
接下来来看下每个区别到底是怎么回事。
区别1:响应式原理不同
Vue2 用的是 Object.defineProperty,它通过代理对象的已有属性,在对属性进行get和set的时候,进行响应式操作。这个对属性的代理必须保证属性是已有的,所以临时添加一个属性,是没有被代理的,自然也就无法进行响应式。而删除一个属性,不会触发get和set,自然也没有响应式。而且删除属性后,属性被彻底移除,再操作这个属性,相当于新增属性,也不会触发响应式。
javascript
const o = {}; // 创建一个新对象
// 通过 defineProperty 使用访问器属性描述符添加对象属性的示例
let bValue = 38;
Object.defineProperty(o, "b", {
get() {
return bValue;
},
set(newValue) {
bValue = newValue;
console.log("set o.b")
},
});
// 对b赋值时,触发set
o.b = 42
// 输出
// set o.b
// 新增一个属性,无输出
o.c = 1
//无输出
对于数组的响应式,理论上每个下标也是一个特殊的数字属性,但是,数组通常变化频繁,且经常会创建稀疏数组,这样一来,对不一定用到的下标索引全部加上响应式代理,很明显是造成了性能的浪费,所以Vue2重写了数组原型上的push、pop、shift、unshift、splice、sort、reverse,这七个数组的操作方法,方法内部手动触发依赖更新。
Vue3 用的是Proxy,它通过代理对象本身来进行响应式操作,每次调用get/set时,都会传入相应的对象和属性名,这样可以在get/set内部进行对象属性的访问,从而精准的触发每个属性的响应式。而数组的下标索引也是特殊属性,所以也会触发set。从而一举解决Vue2中数组下标赋值不能响应式的问题。
javascript
const o = {b: 38}; // 创建一个新对象
// 通过Proxy代理o
const newO = new Proxy(o, {
get(obj, prop) {
return obj[prop]
},
set(obj, prop, value) {
obj[prop] = value
console.log("set o." + prop)
return ture;
},
})
newO.b = 42 // 输出:set o.b
newO.c = 1 // 输出: set o.c
// 数组响应式
const a = [0, 1, 2];
const newA = new Proxy(a, {
get(obj, prop) {
return obj[prop]
},
set(obj, prop, value) {
obj[prop] = value
console.log("set a[" + prop + "]")
return true;
},
})
newA[0] = 1 // 输出:set a[0]
newA.length = 4 // 输出:set a[length]
newA[3] = 5 // 输出:set a[3]
console.log(a) // 输出[1,1,2,5]
"注意:Proxy 的 set trap 必须返回 true,否则在严格模式或调用数组原生方法时会抛出 TypeError。"
区别2:代码编写不同
Vue2主要使用选项式写法,通过data、props、methods、watch、computed等选项来组织代码,而Vue3可以用setup(),通过组合式写法,将代码按业务逻辑组合在一起,更符合业务代码编写的习惯。
在复杂组件中,一个业务功能(如'用户搜索+筛选+分页')往往涉及 data、methods、watch、computed 多个选项,代码被横向切割 ,阅读和维护成本高。而组合式 API 允许我们将同一逻辑关注点的代码放在一起,甚至封装成可复用的自定义 Hook(如 useUserSearch()),大幅提升内聚性和复用性。
区别3:根节点数量不同
Vue2只能由1个根节点,Vue3可以有多个根节点。这主要是因为Vue2 的虚拟 DOM diff 算法依赖唯一根。而 Vue3 引入了 Fragment(片段) ,内部能处理多个根节点,让模板更灵活,避免无意义的 <div> 包裹。而Fragment只存在于虚拟Dom中,是一层逻辑上的包裹,并不会渲染到真实Dom中。
区别4:生命周期不同
|-------------------|---------------------|
| Vue2 | Vue3 |
| beforeCreate | setup之前 |
| created | setup中 |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeDestroy | onBeforeUnmount |
| destroyed | onUnmounted |
Vue3 对生命周期钩子进行了命名优化和逻辑整合:
- 将 Vue2 的 beforeDestroy 重命名为 onBeforeUnmount
- 将 destroyed 重命名为 onUnmounted
同时,在组合式 API 中,所有生命周期钩子都以函数形式导入(如 onMounted, onUnmounted),并在setup() 中显式调用,使得生命周期逻辑可以按关注点与业务代码组织在一起,而不是分散在选项中。
区别5:编译与渲染性能优化机制不同
Vue3 在编译阶段对模板进行深度静态分析,并引入多项运行时优化:
- PatchFlags(补丁标记):为每个虚拟节点标记动态部分(如文本、props),使 diff 时只关注变化内容;
- Block Tree(区块树):将模板划分为独立区块,更新时仅追踪动态子树,跳过静态区域;
- 静态提升(Hoisting):将纯静态节点提取到渲染函数外部,只创建一次,后续直接复用;
- 事件处理器缓存:自动缓存内联函数,避免因引用变化导致子组件不必要更新。
这些优化使得 Vue3 的虚拟 DOM 更新性能显著优于 Vue2,尤其在大型组件中效果明显。