目录
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 的响应式原理和数据双向绑定机制至关重要。在大型前端应用中,了解依赖收集的过程以及如何管理依赖关系是非常有益的,尤其是在构建自定义组件或处理复杂的数据流时。
案例需求
假设有一个用户界面,其中包含一个数字输入框和一个显示框。输入框中的值会影响显示框中的内容。我们希望在输入框的值改变时,显示框能够自动更新。
classDep{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;// 全局的当前正在计算的WatcherclassWatcher{constructor(){
Dep.target =this;}addDep(dep){
dep.subs.push(this);// 将当前Watcher加入到Dep的subs数组中}update(){// 执行更新操作}}// 示例用法let dep =newDep();let watcher =newWatcher();
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>exportdefault{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
数组中。
classWatcher{
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)}...}functionpushTarget(_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())}newWatcher(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就能够得到通知,并进行相应的更新操作,从而确保视图能够及时地反映数据的变化。
版权归原作者 星辰迷上大海 所有, 如有侵权,请联系我们删除。