MST

星途 面试题库

面试题:Vue 2与Vue 3响应式系统中defineProperty和Proxy的区别

请阐述在Vue 2中使用defineProperty以及Vue 3中使用Proxy实现响应式系统的基本原理,并详细说明二者在使用场景、性能、数据劫持的粒度等方面有哪些区别。
16.9万 热度难度
前端开发Vue

知识考点

AI 面试

面试题答案

一键面试

Vue 2中defineProperty实现响应式系统原理

  1. 数据劫持:通过Object.defineProperty()方法,对对象的每个属性进行数据劫持。例如:
let data = { name: 'John' };
Object.keys(data).forEach(key => {
  let value = data[key];
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get() {
      return value;
    },
    set(newValue) {
      if (newValue!== value) {
        value = newValue;
        // 通知视图更新
        console.log('属性值更新,通知视图更新');
      }
    }
  });
});
  1. 依赖收集:在get方法中收集依赖,通常是通过一个Dep类来管理依赖。每个属性对应一个Dep实例,当属性被访问时,将当前的Watcher实例添加到Dep中。
  2. 派发更新:在set方法中,当属性值变化时,遍历Dep中收集的依赖(Watcher实例),调用Watcher的更新方法,通知视图更新。

Vue 3中Proxy实现响应式系统原理

  1. 数据劫持:使用Proxy对象创建一个代理,拦截对象的操作。例如:
let data = { name: 'John' };
let proxy = new Proxy(data, {
  get(target, property) {
    // 依赖收集
    console.log('属性被访问,进行依赖收集');
    return target[property];
  },
  set(target, property, value) {
    if (target[property]!== value) {
      target[property] = value;
      // 通知视图更新
      console.log('属性值更新,通知视图更新');
      return true;
    }
    return false;
  }
});
  1. 依赖收集:在get拦截函数中进行依赖收集,与Vue 2类似,但实现方式有所不同。
  2. 派发更新:在set拦截函数中,当属性值变化时,通知相关依赖进行更新。

二者区别

  1. 使用场景
    • Vue 2(defineProperty):对于已有对象的属性劫持比较方便,但对于新增属性和删除属性需要特殊处理(使用Vue.setVue.delete)。
    • Vue 3(Proxy):对整个对象进行代理,新增和删除属性不需要特殊处理,更适合复杂数据结构的响应式处理。
  2. 性能
    • Vue 2(defineProperty):在初始化时,对每个属性进行遍历劫持,对于大型对象性能开销较大。并且在新增和删除属性时,也会有额外性能开销。
    • Vue 3(Proxy):初始化时只创建一个代理,性能相对较好。而且由于对新增和删除属性的处理更自然,在操作对象结构时性能也更优。
  3. 数据劫持的粒度
    • Vue 2(defineProperty):只能对对象的单个属性进行劫持。
    • Vue 3(Proxy):可以对整个对象进行劫持,对对象内深层次的嵌套结构也能自然处理,数据劫持粒度更粗但更灵活。