Skip to content

Vue3 和 Vue2 的比较

Vue2 也有响应式数据,Vue3 的响应式数据有了哪些具体的不同?核心本质原理是什么?

Vue3 相较于 Vue2,在响应式数据的设计和实现上有了非常显著的升级。这些变化不仅提升了性能,还增强了功能和灵活性。以下从具体不同和核心原理两方面分析:

Vue3 和 Vue2 响应式数据的具体不同

1.1 核心响应式机制的实现方式

特性Vue2Vue3
核心机制基于 Object.defineProperty 的数据劫持基于 Proxy 的数据拦截
劫持范围只能拦截对象已有的属性可以拦截所有操作,包括新增、删除、嵌套等
监听的局限性无法监听对象属性的新增和删除- 数组变动支持有限完全支持对象和数组的新增、删除、嵌套等变化
性能数据量大时性能下降更高效,性能几乎不受数据量的影响

解释:

  • Vue2 的 Object.defineProperty 只能劫持已存在的属性,因此需要通过递归预先处理整个对象,这导致了性能问题和不灵活性。
  • Vue3 使用 Proxy 拦截所有操作,包括新增和删除属性、数组的索引变化等,无需提前递归,灵活性和性能更高。

1.2 响应式 API

特性Vue2Vue3
响应式创建方式使用 data 配置创建响应式对象 Vue.set() 手动新增属性通过 reactive() 创建响应式对象 ref() 支持单值响应式
响应式对象只能操作对象,不能对基本数据类型创建响应式支持对基本数据类型、对象和数组创建响应式
独立管理响应式响应式状态与组件绑定响应式状态可以独立存在于组件外,支持组合式逻辑

解释:

  • Vue2 中的响应式依赖于组件的生命周期,响应式逻辑较难复用。
  • Vue3 引入了 reactive() 和 ref(),可以独立于组件管理响应式状态,支持更灵活的逻辑复用和更好的代码结构。

1.3 响应式性能优化

特性Vue2Vue3
依赖收集粒度按组件级别收集依赖按属性级别收集依赖,精细化更新
性能表现对大量状态的变化,可能引起多余的重新渲染精确更新,避免多余的渲染

解释:

  • Vue2 的依赖收集是以整个组件为单位,任何一个响应式属性的变化都可能触发整个组件的重新渲染。
  • Vue3 通过按属性收集依赖,只有依赖的属性发生变化时才会触发更新,大幅优化了性能。

Vue3 响应式的核心本质原理

Vue3 的响应式机制核心是基于 JavaScript 的 Proxy 和 ES6 的 WeakMap 数据结构,其关键思想如下:

2.1 Proxy 的原理

  • Proxy 是 ES6 提供的一个 API,用于拦截对象的操作(如读取、写入、删除、遍历等),并允许自定义这些操作的行为。
  • Vue3 中通过 Proxy 拦截对象的操作,并在访问或修改时动态进行依赖收集和触发更新。

Proxy 的优势:

  1. 灵活性更强:

    • 可拦截对象属性的新增、删除操作。
    • 支持对嵌套对象的代理,不需要递归处理。
  2. 性能优化:

    • 拦截时按需处理,无需预先遍历整个对象。

示例:

js
const data = { name: 'Vue', age: 3 };
const proxy = new Proxy(data, {
  get(target, key) {
    console.log(`获取属性 ${key}`);
    return target[key];
  },
  set(target, key, value) {
    console.log(`设置属性 ${key} 为 ${value}`);
    target[key] = value;
    return true;
  },
});

proxy.name; // 输出: 获取属性 name
proxy.age = 4; // 输出: 设置属性 age 为 4

2.2 WeakMap 依赖追踪机制

  • Vue3 使用 WeakMap 来记录响应式对象及其依赖关系。
  • WeakMap 的结构:
    • 键:响应式对象的属性。
    • 值:依赖该属性的所有副作用(如组件更新函数、watch 监听器等)。

工作原理:

  1. 当访问响应式属性时,Vue3 会将依赖的副作用函数存入 WeakMap 中。
  2. 当修改响应式属性时,Vue3 会从 WeakMap 中找到对应的副作用函数并执行更新。

示例:

js
const targetMap = new WeakMap();

function track(target, key) {
  let depsMap = targetMap.get(target);
  if (!depsMap) {
    depsMap = new Map();
    targetMap.set(target, depsMap);
  }
  let dep = depsMap.get(key);
  if (!dep) {
    dep = new Set();
    depsMap.set(key, dep);
  }
  dep.add(effect); // 依赖收集
}

function trigger(target, key) {
  const depsMap = targetMap.get(target);
  if (!depsMap) return;
  const dep = depsMap.get(key);
  if (dep) {
    dep.forEach(effect => effect()); // 触发更新
  }
}
  1. Vue2 和 Vue3 的总结对比
特性Vue2Vue3
核心实现Object.definePropertyProxy
依赖收集粒度组件级别属性级别
性能大量响应式数据可能性能下降高效拦截,支持大规模复杂数据
响应式创建方式data 配置reactive() 和 ref() 更灵活
状态逻辑复用难以复用支持组合式 API,更易复用和模块化
适用场景适合简单的组件逻辑适合复杂的项目和动态需求

Vue3 的响应式系统从根本上更灵活、更高效,也为开发者提供了更多的自由度和可能性,比如状态管理、逻辑抽离、跨组件共享等,可以说是 Vue 框架响应式系统的一次重大革新。

Vue3 中, 为何不需要再用 vue.set() 了?

是的,在 Vue 3 中,已经不需要再使用 Vue.set() 了,因为 Vue 3 的响应式系统基于 Proxy,已经天然支持动态属性的新增和删除操作。

为什么 Vue 3 不需要 Vue.set()?

  1. Vue 2 的限制:

    • Vue 2 的响应式基于 Object.defineProperty。
    • 无法检测对象新增或删除属性的操作,只能在创建对象时监听已有属性。
    • 为了解决这个问题,Vue 2 提供了 Vue.set() 和 Vue.delete(),专门用来手动触发响应式更新。
  2. Vue 3 的改进:

    • Vue 3 使用 Proxy 拦截所有操作(get、set、delete 等)。
    • 动态添加或删除属性,依赖追踪和更新通知会自动处理。
    • 这使得 Vue 3 无需额外的工具方法(如 Vue.set() 和 Vue.delete())。

示例对比

Vue 2 的做法

js

// 需要 Vue.set()
this.$set(this.obj, 'newProp', 'newValue');

// 如果直接赋值:不会触发响应式更新
this.obj.newProp = 'newValue'; // 无法监听

Vue 3 的做法

// 不需要 Vue.set()
this.obj.newProp = 'newValue'; // 自动触发响应式更新

注意点:

  1. 新增属性:

    • 在 Vue 3 中直接赋值即可触发响应式更新,无需调用额外的 API。
  2. 删除属性:

    • 同样可以直接使用 delete 操作符:
    js
    delete this.obj.prop; // 自动更新
  3. 深层嵌套的响应式对象:

    • Vue 3 的 Proxy 对深层对象也会自动代理,无需递归处理。

总结:

在 Vue 3 中,响应式系统更加自然和直观,开发者可以直接使用原生的 JavaScript 操作(如新增属性、删除属性等),无需再使用 Vue.set() 和 Vue.delete()。