在对象的响应式原理中,有三大角色:Observer、Dep、Watcher。
Observer
Observer 负责将一个普通对象转换为响应式对象,也就是给每一个属性添加 getter 和 setter。
在我们读取属性时,getter 会将 Watcher 添加到 Dep 中去。一个 Watcher 相当于一个同学,Dep 相当于一个花名簿。
在我们修改属性时,setter 会将 newValue 与 oldValue 相比较,并进行值的修改。同时让花名簿去通知每一个同学去做一些事。
具体通知同学做什么事呢?让我们去往 Dep 和 Watcher 中一探究竟!
Watcher(影分身)
我们假设一个同学就是一个属性。
我们再假设每个同学都学会了影分身,那么每个同学可能都会有多个影分身,这个影分身就是我们的 Watcher 实例。
也就是说一个属性可能有多个 Watcher 实例。
假设你分别在 watch、template、computed 中书写了三次某个属性。那么就会为这个同学创建出三个影分身。
Dep(花名簿)
一个属性只有一个花名簿,一个花名簿自然可以对应多个影分身。这里的花名簿就是我们的 Dep 实例,它负责收集依赖该属性的所有 Watcher 实例(影分身)。Dep 实例中存储了一个 Watcher 实例数组,用于记录与该属性相关的所有影分身。
当我们修改某个属性值时,Dep 实例会通知花名簿中记录的所有影分身(Watcher 实例) ,让它们执行相应的操作。这些操作可能包括更新 DOM、重新计算计算属性、执行自定义的回调函数等。
通过这样的设计,Vue 可以准确地跟踪数据属性与各种使用场景(模板、计算属性、手动监听等)之间的依赖关系,并确保数据与视图保持同步。这使得 Vue 应用具有很高的可扩展性和灵活性,开发者可以轻松地在不同组件和场景中实现响应式行为。
案例分析
接下来我们将分析一个响应式属性 a:{name:"小明"}在 Vue 中的历程,以便让大家更加深入地理解响应式原理。在此过程中,我们将使用花名簿(Dep)、同学(属性)和影分身(Watcher)的概念来讲解,使内容更易理解。
1. 创建 Vue 实例
首先,我们创建一个 Vue 实例,并将响应式属性 a 添加到 data 对象中:
new Vue({
data: {
a: { name: "小明" }
}
});
2. Observer 转换响应式对象
在 Vue 实例创建的过程中,Vue 会使用 Observer 类将 data 对象中的普通对象转换为响应式对象。这意味着同学 a 的属性 name(即小明)会被添加 getter 和 setter。
3. Watcher(影分身)的创建
当我们在模板、计算属性和 watch 属性中使用响应式属性 a 时,Vue 会分别为这些场景创建相应的影分身(Watcher 实例)。假设我们在这三个场景中都使用了属性 a,那么小明就会有三个影分身。
4. Dep(花名簿)收集影分身
当我们访问属性 a.name 时,getter 会被触发。在 getter 中,Vue 会将当前活跃的影分身(Watcher 实例)添加到小明对应的花名簿(Dep 实例)中。这样,我们就建立了同学 a(属性)和影分身(Watcher 实例)之间的联系。
5. 属性值变更
当我们修改同学 a 的名字(即 a.name 的值)时,setter 会被触发。在 setter 中,Vue 会判断新名字是否与旧名字相同。如果新名字与旧名字不同,Vue 会更新名字,并通知花名簿(Dep 实例)。
6. 花名簿通知影分身
花名簿(Dep 实例)收到通知后,会遍历所有与小明相关的影分身(Watcher 实例),通知它们执行相应的操作。这些操作可能包括:
- 更新 DOM(模板中使用的 a.name)
- 重新计算计算属性(计算属性中依赖的 a.name)
- 执行自定义回调函数(watch 属性中监听的 a.name)
通过这个以花名簿、同学、影分身的概念为基础的解释,因为代码讲解可能过于繁琐,不利于小白理解,所以我采取了纯文字的形式带大家了解响应式原理。
最后,本文用于记录自己的学习历程,欢迎大家指出错误。如果大家有更多想了解的 ,可以给我留言~。