行业知识
前端Vue篇之Vue是如何收集依赖的?
目录
- Vue是如何收集依赖的?
- 1. `Dep` 类
- 1. 静态属性 `target`
- 2. subs 属性
- 3. 方法:addSub 和 removeSub
- 4. 方法: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 类的作用将有助于我们更好地设计数据流和组件通信。
{{ calculatedValue }} export default { data() { return { value: 0, calculatedValue: 0 }; }, mounted() { this.$watch( () => this.value, () => { this.calculatedValue = this.value * 2; // 示例:当输入框数值改变时触发更新 } ); }, methods: { updateOtherSections() { // 更新其他相关部分的逻辑 } }};- 在 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就能够得到通知,并进行相应的更新操作,从而确保视图能够及时地反映数据的变化。
-
上一篇:米诺斯服务器怎么进?
下一篇:方舟服务器备份在哪里?