Vue.js 响应式系统的基本原理
- 数据劫持:Vue 通过
Object.defineProperty
方法劫持数据对象的属性,使其变成 getter 和 setter。当数据被访问或修改时,Vue 会记录下这些操作,并在数据变化时通知订阅者(通常是视图)进行更新。 - 依赖收集:当渲染函数依赖于某个数据时,Vue 会在数据的 getter 中收集渲染函数作为依赖。这样,当数据变化时,Vue 可以知道哪些渲染函数需要更新。
- 观察者(Observer):Vue 使用观察者模式来监视数据对象的变化。每个数据对象都有一个对应的 Observer 实例,它负责维护数据的响应性。
- 编译器(Compiler):Vue 的编译器负责将模板编译成渲染函数。在编译过程中,编译器会分析模板中的表达式,并注册依赖。
computed 属性的原理
computed
属性是一个基于依赖的数据自动重新计算的 getter 函数。当依赖的数据发生变化时,computed
会重新计算其值,并将结果缓存起来
实现细节:
- 依赖追踪:当计算
computed
属性时,Vue 会自动追踪其依赖的数据。这意味着,如果computed
属性内部访问了某个响应式数据,那么这个数据就会被添加到computed
属性的依赖列表中。 - 缓存机制:
computed
属性的结果会被缓存。只有当依赖的数据发生变化时,computed
才会被重新计算。这是通过一个称为 Dep(依赖收集器)的机制实现的。 - getter 和 setter:
computed
属性本质上是一个 getter 函数,它可以在模板或计算属性中被访问。如果需要设置值,可以通过setter
来实现,但这通常不是computed
的主要用途
watch 监听器的原理
watch
是用来监听某个特定的数据变化,并在变化发生时执行某些操作。watch
不像 computed
那样缓存结果,而是更灵活地处理副作用。
实现细节:
- 侦听器(Watcher):
watch
使用 Watcher 类来监听数据的变化。当数据发生变化时,Watcher 会触发回调函数。 - 立即执行:每当被监听的数据变化时,就会立即执行相应的函数。这意味着
watch
回调函数会在数据变化时立即执行,而不考虑是否有必要重新计算。 - 深度监听:
watch
支持深度监听,这意味着它可以监听对象内部属性的变化。这需要在配置watch
时显式指定deep: true
。 - 立即调用:可以通过
immediate: true
选项使watch
回调在组件初始化时立即执行一次
总结
computed
适用于创建派生数据,并且只有在依赖的数据变化时才会重新计算,具有缓存机制。watch
适用于处理数据变化时的副作用逻辑,如异步操作或其他数据的更新,每次数据变化都会立即执行回调函数。
Watcher
类与 Object.defineProperty
的关系
Watcher
类和 Object.defineProperty
的关系体现在数据依赖的追踪和响应上:
- 依赖收集:当
Watcher
实例在创建时访问某个数据时,Object.defineProperty
定义的 getter 会触发,并将当前Watcher
实例添加到该数据的依赖列表中(通过Dep
类)。 - 依赖更新:当数据发生变化时,
Object.defineProperty
定义的 setter 会触发,并通知所有依赖该数据的Watcher
实例更新。Watcher
会重新计算其依赖的值,并根据需要执行更新逻辑。
computed
属性中的应用
在 computed
属性中,Watcher
类被用来自动追踪依赖的数据。当 computed
属性被首次访问时,Vue 会创建一个 ComputedWatcher
实例,并在访问依赖的数据时自动收集依赖。
export default {
data() {
return {
a: 1,
b: 2
};
},
computed: {
sum() {
return this.a + this.b;
}
}
};
在这个例子中,当 sum
被首次访问时,Vue 会创建一个 ComputedWatcher
实例来追踪 a
和 b
的变化。当 a
或 b
发生变化时,ComputedWatcher
会重新计算 sum
的值,并更新依赖于 sum
的视图。