深入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源码也会后续更新出来,喜欢的点点关注。

相关文章
|
28天前
|
JavaScript API
Vue3中的计算属性能否动态修改
【9月更文挑战第5天】Vue3中的计算属性能否动态修改
75 10
|
6天前
|
缓存 JavaScript 前端开发
vue-day02计算属性,v-bind,v-if,v-for
文章介绍了Vue.js中的计算属性、class与style的绑定、条件渲染和列表渲染的使用。通过示例代码展示了如何使用计算属性简化模板逻辑、如何通过v-bind动态绑定class和style、如何使用v-if进行条件渲染以及如何使用v-for进行列表渲染。这些特性使得Vue.js在构建动态用户界面时更加灵活和强大。
vue-day02计算属性,v-bind,v-if,v-for
|
6天前
|
JavaScript
vue 计算属性,实现复选框的全选和反选 【小案例】
本文通过一个Vue.js小案例,展示了如何使用计算属性实现复选框的全选和反选功能。计算属性的完整写法包括get和set两部分,分别用于获取值和设置值。在全选和反选的场景中,计算属性的get方法用于判断所有复选框是否都已选中,从而控制全选复选框的状态;计算属性的set方法则用于根据全选复选框的状态,批量更新每个复选框的选中状态。通过示例代码和效果图,文章清晰地说明了计算属性在实现这一功能中的作用和效果。
vue 计算属性,实现复选框的全选和反选 【小案例】
|
8天前
如何在 Vue3 中创建一个计算属性?
如何在 Vue3 中创建一个计算属性?
19 1
|
14天前
|
JavaScript API
vue学习(13)监视属性
vue学习(13)监视属性
28 2
|
28天前
|
JavaScript API
如何使用Vue3的可计算属性
【9月更文挑战第5天】如何使用Vue3的可计算属性
48 13
|
20天前
|
缓存 JavaScript
vue学习(12)计算属性
vue学习(12)计算属性
17 3
|
20天前
|
缓存 JavaScript
vue学习(12)计算属性
vue学习(12)计算属性
28 2
|
6天前
|
JavaScript API
模块化妙用!用vue3实现一个鼠标追踪器和异步加载组件
该文章展示了如何使用Vue3的Composition API实现鼠标追踪器功能,并介绍了创建异步加载组件的方法,利用TS泛型增强了组件的灵活性与可维护性。
|
2月前
|
JavaScript 前端开发
vue获取元素属性
vue获取元素属性
41 3
下一篇
无影云桌面