前端Vue篇之Vue是如何收集依赖的?

本文详细介绍了Vue中的依赖收集机制,包括Dep类的结构与功能、Watcher类的构造与更新,以及Vue类如何通过updateComponent函数进行视图更新。重点讲解了Dep、Watcher在数据变化时的角色和相互作用,帮助理解Vue响应式系统的工作原理。
摘要由CSDN通过智能技术生成


Vue是如何收集依赖的?

在 Vue 中,依赖收集是一个重要的概念,它是 Vue 实现响应式数据的核心。当一个组件被初始化时,Vue 会对组件的 data 进行初始化,将普通的 JavaScript 对象变成响应式对象。在这个过程中,Vue 会进行依赖收集,以便在数据发生变化时,能够通知到所有依赖这个数据的地方。

依赖收集的过程主要涉及到三个类:DepWatcherVue

1. Dep

Dep 是依赖收集的核心,它的主要作用是管理所有的 WatcherDep 类中有一个静态属性 target,它指向当前正在计算的 Watcher,保证了同一时间全局只有一个 Watcher 被计算。Dep 类中还有一个 subs 属性,它是一个 Watcher 的数组,用来存储所有依赖这个 DepWatcher

1. 静态属性 target

Dep 类中有一个静态属性 target。这个属性的作用是指向当前正在计算的 Watcher,它的存在保证了在同一时间内全局只有一个 Watcher 被计算。你可以把它想象成一个焦点,告诉系统当前需要关注哪个 Watcher

2. subs 属性

另一个属性是 subs,它是一个存储 Watcher 的数组。这意味着 Dep 类实际上就是对 Watcher 的管理者。当数据发生变化时,Dep 就会通知所有依赖于它的 Watcher 进行更新,而这些 Watcher 则会负责相应的视图更新。

3. 方法:addSub 和 removeSub

addSub 方法用于将 Watcher 添加到 subs 数组中,而 removeSub 方法则是用来从 subs 数组中移除对应的 Watcher

4. 方法:depend 和 notify

  • depend 方法的作用是在计算属性或者渲染过程中建立依赖关系。如果当前存在正在计算的 Watcher(即 Dep.target 存在),那么就会将当前的 Dep 与该 Watcher 建立关联。
  • notify 方法则是在数据发生变化时通知所有依赖这个 DepWatcher 执行更新操作。它遍历 subs 数组中的所有 Watcher,并逐一触发它们的更新方法。

在实际工作项目中,Dep 类的概念对于理解 Vue.js 的响应式原理和数据双向绑定机制至关重要。在大型前端应用中,了解依赖收集的过程以及如何管理依赖关系是非常有益的,尤其是在构建自定义组件或处理复杂的数据流时。

案例需求

假设有一个用户界面,其中包含一个数字输入框和一个显示框。输入框中的值会影响显示框中的内容。我们希望在输入框的值改变时,显示框能够自动更新。

class Dep {
  constructor() {
    this.subs = []; // 存储所有依赖这个Dep的Watcher
  }

  depend() {
    if (Dep.target) {
      Dep.target.addDep(this); // 将当前Dep与正在计算的Watcher关联起来
    }
  }

  notify() {
    this.subs.forEach(watcher => {
      watcher.update(); // 通知所有依赖这个Dep的Watcher进行更新
    });
  }
}

Dep.target = null; // 全局的当前正在计算的Watcher

class Watcher {
  constructor() {
    Dep.target = this;
  }

  addDep(dep) {
    dep.subs.push(this); // 将当前Watcher加入到Dep的subs数组中
  }

  update() {
    // 执行更新操作
  }
}

// 示例用法
let dep = new Dep();
let watcher = new Watcher();
dep.depend(); // 将当前Dep与正在计算的Watcher关联起来
dep.notify(); // 通知所有依赖这个Dep的Watcher进行更新
  1. 创建Dep类,其中包括subs数组和depend、notify方法。
  2. 创建Watcher类,其中包括addDep和update方法。
  3. 在Dep类中,通过target属性指向当前正在计算的Watcher,在depend方法中将当前Dep与正在计算的Watcher关联起来,在notify方法中通知所有依赖这个Dep的Watcher进行更新。
  4. 在Watcher类中,通过addDep方法将当前Watcher加入到Dep的subs数组中,在update方法中执行更新操作。
  • Dep类和Watcher类是数据响应式系统的核心,理解其原理对于理解Vue等前端框架的工作原理非常重要。
  • 在实际项目中,可以根据具体需求对Dep类和Watcher类进行定制和扩展,以满足不同的业务场景需求。

案例需求

假设我们有一个需求,希望在用户修改某个输入框的数值时,页面上的其他相关部分能够实时更新。这就需要利用 Vue.js 的响应式系统来实现数据的自动更新。在这种情况下,理解 Dep 类的作用将有助于我们更好地设计数据流和组件通信。

<template>
  <div>
    <input v-model="value" @input="updateOtherSections" />
    <div>{{ calculatedValue }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: 0,
      calculatedValue: 0
    };
  },
  mounted() {
    this.$watch(
      () => this.value,
      () => {
        this.calculatedValue = this.value * 2; // 示例:当输入框数值改变时触发更新
      }
    );
  },
  methods: {
    updateOtherSections() {
      // 更新其他相关部分的逻辑
    }
  }
};
</script>
  1. mounted 钩子中,通过 this.$watch 监听 value 的变化。
  2. value 变化时,会触发相应的更新函数,计算新的值并更新到 calculatedValue
  3. 用户输入框的值变化会引起 value 的变化,从而触发 calculatedValue 的更新。

总结

Dep 类通过管理依赖关系,用于管理所有的Watcher,确保了数据变化时相关的视图能够得到更新。它利用 subs 数组存储依赖它的 Watcher,并通过 notify 方法通知它们进行更新。同时,使用静态属性 target 确保了在同一时间内只有一个全局的 Watcher 被计算。通过理解 Dep 类的作用,我们可以更好地设计和构建具有复杂数据流的 Vue 组件,并更好地应对实际项目中的需求。

2. Watcher

Watcher 是一个用来计算表达式的类。在 Watcher 的构造函数中,它会执行表达式,这个表达式可能会触发数据的 getter,从而进行依赖收集。Watcher 类中还有一个 addDep 方法,它会将当前的 Watcher 添加到 Depsubs 数组中。

class Watcher {
  getter;
  ...
  constructor (vm, expression){
    ...
    this.getter = expression;
    this.get();
  }
  get () {
    pushTarget(this);
    value = this.getter.call(vm, vm)
    ...
    return value
  }
  addDep (dep){
        ...
    dep.addSub(this)
  }
  ...
}
function pushTarget (_target) {
  Dep.target = _target
}

构造函数

Watcher 类有一个构造函数,它接受两个参数:vmexpression。在构造函数中,将传入的 expression 赋值给 getter 属性,并立即调用 get 方法。

get 方法

  • get 方法是 Watcher 类中最关键的方法之一。当 Watcher 被创建时,会立即调用 get 方法。
  • get 方法内部,会将当前的 Watcher 推入一个全局的位置(通过 pushTarget 函数),然后执行 expression,这个 expression 可能是一个函数或者计算属性的表达式。
  • 在执行过程中,会触发对应数据的 getter,从而建立起依赖关系,并且触发了依赖收集过程。

addDep 方法

  • addDep 方法用于将当前的 Watcher 添加到指定的 Dep 实例中,建立起 WatcherDep 之间的关联。
  • 这样,在数据变化时,Dep 就能够通知到相应的 Watcher 进行更新操作。

pushTarget 函数

pushTarget 函数的作用是将当前的 Watcher 设置为全局唯一的 Dep.target,以确保在同一时间内全局只有一个 Watcher 被计算。

  • get 方法中,通过调用 expression 来触发数据的 getter,从而建立起依赖关系。
  • 使用 addDep 方法将当前的 Watcher 添加到对应的 Dep 实例中,确保在数据变化时能够及时通知到相关的 Watcher 进行更新操作。

总结

Watcher 类通过 get 方法建立了依赖关系,并且在初始化时会立即执行 expression,从而触发对应数据的 getter,进而进行依赖收集。通过 addDep 方法,WatcherDep 建立了关联,确保了数据变化时能够及时通知到相关的 Watcher 进行更新操作。

3. Vue

Vue 类是 Vue 的入口,它的主要作用是初始化 Vue 应用。在 Vue 类的初始化过程中,会对组件的 data 进行初始化,将普通的 JavaScript 对象变成响应式对象。在这个过程中,会进行依赖收集。

依赖收集的过程如下:

  1. 首先,Vue 会对组件的 data 进行初始化,将普通的 JavaScript 对象变成响应式对象。

  2. 然后,Vue 会实例化一个 Watcher,并执行 Watcherget 方法。

  3. get 方法中,Watcher 会执行表达式,这个表达式可能会触发数据的 getter,从而进行依赖收集。

  4. getter 中,会调用 Dep.target.addDep(this),将当前的 Watcher 添加到数据的 Depsubs 数组中。

  5. 这样,当数据发生变化时,Dep 就可以通过 subs 数组,找到所有依赖这个数据的 Watcher,并通知它们数据发生了变化。

这就是 Vue 如何进行依赖收集的过程。

updateComponent = () => {
  vm._update(vm._render())
}
new Watcher(vm, updateComponent)

updateComponent 函数

updateComponent 函数是一个箭头函数,它的作用是更新 Vue 实例的视图。在这个函数内部,它调用了 vm._render() 方法生成虚拟 DOM,并将其传递给 vm._update() 方法来更新实际的 DOM。

创建 Watcher

通过 new Watcher(vm, updateComponent) 创建了一个新的 Watcher 实例。这里的 vm 是 Vue 实例,updateComponent 是上面定义的更新函数。

在创建 Watcher 实例时,会立即执行 updateComponent 函数,这样就建立了依赖关系,updateComponent 函数中所使用的数据发生变化时,对应的 Watcher 就能够收到通知,并触发更新操作。

  • 当创建 Watcher 实例时,会立即执行 updateComponent 函数,从而进行依赖收集并建立起对应的关联。
  • updateComponent 函数的执行将触发 Vue 实例的重新渲染过程,确保视图能够及时地响应数据的变化。

总结

通过创建 Watcher 实例,将 updateComponent 函数与 Vue 实例建立了关联,确保了当相关数据发生变化时,能够触发视图的更新操作。这种机制保证了 Vue 的响应式系统能够自动追踪数据的变化,并及时地更新视图,从而实现了数据驱动视图的效果。

持续学习总结记录中,回顾一下上面的内容:
Vue通过在数据属性的getter中收集依赖。当一个数据被访问时,Watcher会被添加到依赖列表中。这样,当数据变化时,Watcher就能够得到通知,并进行相应的更新操作,从而确保视图能够及时地反映数据的变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星儿(AIGC探索者)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值