目录
Vue是如何收集依赖的?1. `Dep` 类1. 静态属性 `target`2. subs 属性3. 方法:addSub 和 removeSub4. 方法:depend 和 notify案例需求案例需求总结 2. `Watcher` 类构造函数get 方法addDep 方法pushTarget 函数总结 3. `Vue` 类updateComponent 函数创建 Watcher总结
Vue是如何收集依赖的?
在 Vue 中,依赖收集是一个重要的概念,它是 Vue 实现响应式数据的核心。当一个组件被初始化时,Vue 会对组件的 data 进行初始化,将普通的 JavaScript 对象变成响应式对象。在这个过程中,Vue 会进行依赖收集,以便在数据发生变化时,能够通知到所有依赖这个数据的地方。
依赖收集的过程主要涉及到三个类:Dep
、Watcher
和 Vue
。
1. Dep
类
Dep
是依赖收集的核心,它的主要作用是管理所有的 Watcher
。Dep
类中有一个静态属性 target
,它指向当前正在计算的 Watcher
,保证了同一时间全局只有一个 Watcher
被计算。Dep
类中还有一个 subs
属性,它是一个 Watcher
的数组,用来存储所有依赖这个 Dep
的 Watcher
。
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
方法则是在数据发生变化时通知所有依赖这个 Dep
的 Watcher
执行更新操作。它遍历 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; // 全局的当前正在计算的Watcherclass 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进行更新
创建Dep类,其中包括subs数组和depend、notify方法。创建Watcher类,其中包括addDep和update方法。在Dep类中,通过target属性指向当前正在计算的Watcher,在depend方法中将当前Dep与正在计算的Watcher关联起来,在notify方法中通知所有依赖这个Dep的Watcher进行更新。在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>
在 mounted
钩子中,通过 this.$watch
监听 value
的变化。当 value
变化时,会触发相应的更新函数,计算新的值并更新到 calculatedValue
。用户输入框的值变化会引起 value
的变化,从而触发 calculatedValue
的更新。 总结
Dep
类通过管理依赖关系,用于管理所有的Watcher,确保了数据变化时相关的视图能够得到更新。它利用 subs
数组存储依赖它的 Watcher
,并通过 notify
方法通知它们进行更新。同时,使用静态属性 target
确保了在同一时间内只有一个全局的 Watcher
被计算。通过理解 Dep
类的作用,我们可以更好地设计和构建具有复杂数据流的 Vue 组件,并更好地应对实际项目中的需求。
2. Watcher
类
Watcher
是一个用来计算表达式的类。在 Watcher
的构造函数中,它会执行表达式,这个表达式可能会触发数据的 getter
,从而进行依赖收集。Watcher
类中还有一个 addDep
方法,它会将当前的 Watcher
添加到 Dep
的 subs
数组中。
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
类有一个构造函数,它接受两个参数:vm
和 expression
。在构造函数中,将传入的 expression
赋值给 getter
属性,并立即调用 get
方法。
get 方法
get
方法是 Watcher
类中最关键的方法之一。当 Watcher
被创建时,会立即调用 get
方法。在 get
方法内部,会将当前的 Watcher
推入一个全局的位置(通过 pushTarget
函数),然后执行 expression
,这个 expression
可能是一个函数或者计算属性的表达式。在执行过程中,会触发对应数据的 getter
,从而建立起依赖关系,并且触发了依赖收集过程。 addDep 方法
addDep
方法用于将当前的 Watcher
添加到指定的 Dep
实例中,建立起 Watcher
和 Dep
之间的关联。这样,在数据变化时,Dep
就能够通知到相应的 Watcher
进行更新操作。 pushTarget 函数
pushTarget
函数的作用是将当前的 Watcher
设置为全局唯一的 Dep.target
,以确保在同一时间内全局只有一个 Watcher
被计算。
get
方法中,通过调用 expression
来触发数据的 getter
,从而建立起依赖关系。使用 addDep
方法将当前的 Watcher
添加到对应的 Dep
实例中,确保在数据变化时能够及时通知到相关的 Watcher
进行更新操作。 总结
Watcher
类通过 get
方法建立了依赖关系,并且在初始化时会立即执行 expression
,从而触发对应数据的 getter
,进而进行依赖收集。通过 addDep
方法,Watcher
和 Dep
建立了关联,确保了数据变化时能够及时通知到相关的 Watcher
进行更新操作。
3. Vue
类
Vue
类是 Vue 的入口,它的主要作用是初始化 Vue 应用。在 Vue
类的初始化过程中,会对组件的 data 进行初始化,将普通的 JavaScript 对象变成响应式对象。在这个过程中,会进行依赖收集。
依赖收集的过程如下:
首先,Vue
会对组件的 data 进行初始化,将普通的 JavaScript 对象变成响应式对象。
然后,Vue
会实例化一个 Watcher
,并执行 Watcher
的 get
方法。
在 get
方法中,Watcher
会执行表达式,这个表达式可能会触发数据的 getter
,从而进行依赖收集。
在 getter
中,会调用 Dep.target.addDep(this)
,将当前的 Watcher
添加到数据的 Dep
的 subs
数组中。
这样,当数据发生变化时,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就能够得到通知,并进行相应的更新操作,从而确保视图能够及时地反映数据的变化。