深入vue2.0源码系列:依赖追踪与计算属性的实现

简介: 深入vue2.0源码系列:依赖追踪与计算属性的实现

前言

在 Vue.js 中,数据与视图之间的绑定是通过依赖追踪和计算属性来实现的。本文将深入学习 Vue.js 2.0 源码中依赖追踪和计算属性的实现原理。

依赖追踪

在 Vue.js 中,依赖追踪是实现响应式的核心。当一个响应式对象被访问时,Vue.js 会自动追踪这个对象的依赖关系,并将这些依赖关系建立成一个依赖图。当这个响应式对象的值发生改变时,Vue.js 会遍历这个依赖图,通知所有依赖于这个对象的地方更新视图。


Vue.js 中的依赖追踪是通过 Dep 类来实现的。每个响应式对象都对应一个 Dep 对象,它维护了这个响应式对象的所有依赖关系。


具体来说,当一个响应式对象被访问时,会触发它的 getter 函数。在 getter 函数中,会调用 Dep.target 的 addDep 方法,将当前正在计算的计算属性或者渲染 Watcher 添加到这个响应式对象的 Dep 对象的依赖列表中。Dep.target 就是一个全局唯一的 Watcher,它代表当前正在计算的计算属性或者正在渲染的组件的 Watcher。这样就完成了依赖关系的建立。


下面是 Dep 类的简化实现:

class Dep {
  constructor() {
    this.subs = []; // 存储依赖于该响应式对象的所有 Watcher
  }
  depend() {
    if (Dep.target) {
      Dep.target.addDep(this); // 将当前正在计算的 Watcher 添加到依赖列表中
    }
  }
  notify() {
    for (let i = 0; i < this.subs.length; i++) {
      this.subs[i].update(); // 通知依赖于该响应式对象的所有 Watcher 更新视图
    }
  }
  addSub(sub) {
    this.subs.push(sub);
  }
}
Dep.target = null;

上述代码中的 depend 方法用于在 getter 函数中添加依赖关系,notify 方法用于在响应式对象发生改变时通知所有依赖该对象的 Watcher 更新视图。而 addSub 方法则用于添加依赖于该响应式对象的 Watcher。

计算属性

计算属性是 Vue.js 中的一个重要特性,它允许开发者声明式地计算出一个值,并在模板中使用计算属性是通过 Watcher 类来实现的。每个计算属性都对应一个 Watcher 对象,它会自动追踪计算属性的依赖关系,并在依赖的数据发生改变时重新计算值并更新视图。


具体来说,当一个计算属性被访问时,会触发它的 getter 函数。在 getter 函数中,会调用 Dep.target 的 pushTarget 方法,将当前计算属性的 Watcher 设置为全局唯一的 Dep.target。这样,在计算属性的计算过程中,如果访问了响应式数据,就会触发这些响应式数据的 getter 函数,并将当前计算属性的 Watcher 添加到它们的 Dep 对象的依赖列表中。


当计算属性的依赖数据发生改变时,会触发这些数据对应的 Dep 对象的 notify 方法,通知所有依赖于这些数据的 Watcher 更新视图。而这些 Watcher 中,因为计算属性的 Watcher 也被添加到了它们的依赖列表中,所以也会被通知到。这样,计算属性的值就会重新计算,并更新视图。


下面是 Watcher 类的简化实现:

class Watcher {
  constructor(vm, expOrFn, cb) {
    this.vm = vm;
    this.getter = expOrFn;
    this.cb = cb;
    this.deps = [];
    this.depIds = new Set();
    this.value = this.get();
  }
  get() {
    Dep.target = this; // 将当前 Watcher 设置为全局唯一的 Dep.target
    const value = this.getter.call(this.vm); // 触发计算属性的 getter 函数,建立依赖关系
    Dep.target = null; // 恢复全局唯一的 Dep.target
    return value;
  }
  update() {
    const oldValue = this.value;
    this.value = this.get(); // 重新计算计算属性的值
    this.cb.call(this.vm, this.value, oldValue); // 更新视图
  }
  addDep(dep) {
    const id = dep.id;
    if (!this.depIds.has(id)) {
      this.deps.push(dep); // 将依赖于该响应式对象的 Watcher 添加到该响应式对象的依赖列表中
      this.depIds.add(id);
      dep.addSub(this); // 将该 Watcher 添加到该响应式对象的依赖列表中
    }
  }
}

上述代码中的 addDep 方法用于在计算属性的 getter 函数中添加依赖关系,并将该计算属性的 Watcher 添加到依赖的响应式对象的 Dep 对象的依赖列表中。

了解这些原理可以帮助我们更好地理解 Vue.js 的响应式系统,并能更深入地理解计算属性和依赖追踪的机制。同时,我们也可以通过修改 Watcher 的实现方式,实现自定义的响应式行为。


下面是一个简单的例子,展示如何使用 Watcher 自定义响应式行为:

class CustomWatcher extends Watcher {
  constructor(vm, expOrFn, cb) {
    super(vm, expOrFn, cb);
    this.update(); // 在创建 CustomWatcher 时,先手动更新一次视图
  }
  update() {
    const oldValue = this.value;
    this.value = this.get(); // 调用父类的 get 方法计算新值
    this.cb.call(this.vm, this.value, oldValue); // 触发回调函数
  }
}

上述代码中的 CustomWatcher类继承Watcher 类,并重写了 update 方法,将原来的更新视图的逻辑替换成了触发回调函数的逻辑。这样,在创建 CustomWatcher 时,只需要传入计算新值的函数和回调函数,就可以实现自定义的响应式行为。

总结

总结来说,依赖追踪和计算属性是 Vue.js 响应式系统的核心机制。通过依赖追踪,Vue.js 能够自动建立响应式数据之间的依赖关系,并在数据发生改变时自动更新视图。而计算属性则能够让我们将复杂的数据计算逻辑封装起来,以便更好地组织和复用代码。理解这些机制可以帮助我们更好地使用 Vue.js,同时也能够启发我们设计自己的响应式系统。


后续会继续更新vue2.0其他源码系列,包括目前在学习vue3.0源码也会后续更新出来,喜欢的点点关注。

相关文章
|
1月前
|
缓存 JavaScript
|
3天前
|
缓存 JavaScript 开发者
什么是vue的计算属性?为什么使用?计算属性和方法有什么区别?怎样选择
什么是vue的计算属性?为什么使用?计算属性和方法有什么区别?怎样选择
9 0
|
3天前
|
缓存 JavaScript
在 Vue 组件中使用计算属性和侦听器来响应路由变化
Vue Router 中,计算属性和侦听器常用于根据路由变化更新组件状态。计算属性缓存依赖,当路由参数改变时自动更新,如示例中的 `userId`。侦听器则监听 `$route` 变化,执行相应操作,例如在 `currentUserId` 示例中响应 `userId` 更新。计算属性适合简单变化,而异步操作或复杂场景可选用侦听器。Vue 3 中,`watchEffect` 减少了部分侦听场景的复杂性。总之,它们用于组件内部响应路由变化,而非直接处理路由逻辑。
11 4
|
17天前
|
缓存 JavaScript 前端开发
Vue的计算属性和侦听器:computed和watch的使用
【4月更文挑战第24天】Vue.js框架中的计算属性和侦听器是处理数据变化的关键特性。计算属性(computed)基于依赖缓存,仅在相关数据改变时重新计算,适合处理复杂逻辑。例如,计算总价可基于价格和数量动态更新。而侦听器(watch)在数据变化时执行回调,适用于异步操作和开销大的任务。计算属性与侦听器的主要区别在于缓存机制和应用场景,前者用于同步计算,后者用于响应数据变化后的特殊操作。理解这两者有助于优化Vue应用的性能。
|
19天前
|
JavaScript
Vue控制是否禁用disabled属性
Vue控制是否禁用disabled属性
17 1
|
1月前
|
JavaScript
vue 计算属性
vue 计算属性
|
1月前
|
缓存 JavaScript API
「Vue3系列」Vue3 计算属性(computed)、监听属性(watch)
在 Vue 3 中,计算属性(Computed Properties)是一种强大的功能,它允许你声明一个依赖于其他响应式数据属性的属性,并且这个属性的值会根据其依赖的数据的变化而自动更新。计算属性是基于它们的依赖关系进行缓存的,只有在它的相关依赖发生改变时才会重新求值。
37 0
|
1月前
|
缓存 JavaScript
vue中的计算属性和侦听属性的区别
vue中的计算属性和侦听属性的区别
|
1月前
|
缓存 JavaScript
Vue计算属性 computed
Vue计算属性 computed