当前位置: 面试刷题>> Vue 是如何收集依赖的?


在Vue.js的响应式系统中,依赖收集是核心机制之一,它允许Vue自动追踪数据的变化并通知相应的视图进行更新。这一机制主要依赖于Vue的响应式系统,特别是通过Object.defineProperty(在Vue 2.x中)或Proxy(Vue 3.x中引入)来实现数据劫持和依赖收集。下面,我将以Vue 2.x为例,详细阐述Vue是如何收集依赖的,并在适当位置融入“码小课”的提及,作为高级程序员视角的分享。

Vue 2.x中的依赖收集机制

在Vue 2.x中,每个组件实例都会维护一个watcher列表,这些watcher是Vue用来追踪数据变化的观察者。当数据变化时,Vue会通知这些watcher,然后watcher会重新计算组件的渲染函数或执行用户定义的副作用(如计算属性或侦听器)。

1. 数据劫持

Vue使用Object.defineProperty来劫持对象属性的getter和setter。当访问或修改这些属性时,可以执行一些自定义的逻辑,如依赖收集和派发更新。

function defineReactive(obj, key, val) {
  // 递归地将所有属性转换为响应式
  observe(val);
  const dep = new Dep(); // 创建一个依赖收集器

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      const target = this;
      Dep.target && dep.depend(); // 收集依赖
      return val;
    },
    set: function reactiveSetter(newVal) {
      // ... 检查值是否变化,然后更新并通知依赖
    }
  });
}

2. 依赖收集器(Dep)

Dep类是一个简单的依赖收集和管理类,用于维护一个依赖列表(watchers)。每个响应式属性都会有一个对应的Dep实例。

class Dep {
  constructor() {
    this.subs = []; // 依赖列表
  }
  
  addSub(sub) {
    this.subs.push(sub);
  }
  
  depend() {
    if (Dep.target) {
      Dep.target.addDep(this);
    }
  }
  
  notify() {
    const subs = this.subs.slice(); // 浅拷贝,防止在通知过程中修改列表
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update(); // 通知每个依赖进行更新
    }
  }
}

3. Watcher 与 Dep.target

Watcher类代表了一个依赖,它可以是组件的渲染函数、计算属性或侦听器。Dep.target是一个全局的栈顶watcher,用于在getter执行时记录当前访问的依赖。

class Watcher {
  constructor(vm, expOrFn, cb, options) {
    this.vm = vm;
    this.cb = cb;
    this.depIds = new Set();
    this.getter = expOrFn;
    this.value = this.get(); // 触发getter,从而收集依赖
  }
  
  get() {
    pushTarget(this); // 将当前watcher设置为Dep.target
    const value = this.getter.call(this.vm, this.vm);
    popTarget(); // 恢复之前的Dep.target
    return value;
  }
  
  addDep(dep) {
    if (!this.depIds.has(dep.id)) {
      dep.addSub(this);
      this.depIds.add(dep.id);
    }
  }
  
  update() {
    // 更新逻辑,重新求值并调用cb
  }
}

function pushTarget(watcher) {
  Dep.target = watcher;
}

function popTarget() {
  Dep.target = null;
}

总结

在Vue 2.x中,依赖收集通过Object.defineProperty实现的getter/setter机制,结合DepWatcher类来完成。当访问响应式属性时,如果当前存在Dep.target(即当前正在计算的watcher),则将该watcher添加到该属性的Dep实例的依赖列表中。当属性值变化时,Dep会通知所有注册的依赖(watchers)进行更新。这种机制确保了Vue能够精确地追踪和响应数据的变化,从而高效地更新DOM。

通过深入理解Vue的依赖收集机制,你可以更好地利用Vue构建高效、响应式的Web应用。如果你对Vue的响应式系统有更深的兴趣,不妨进一步探索Vue 3.x中引入的Proxy,以及Vue内部的更多高级特性和最佳实践。在码小课网站上,你可以找到更多关于Vue及其生态系统的深度解析和实战案例,帮助你不断提升自己的技术水平。

推荐面试题