computed
⭐️ 计算属性就是对data
里面的数据进行计算得到的一个新的属性。它依赖于用于计算的数据,模板近似于函数调用,那么它和函数有什么区别,内部又是怎么实现的,让我们一起来探索一下计算属性。
计算属性和函数有什么区别
- 在使用时,
computed
可以作为属性,而函数则当做方法调用 computed
可以配置getter
和setter
,因此可以赋值,而函数不行computed
无法接收多个参数,而函数可以computed
具有缓存,而函数没有
接下来深入了解一下 computed 是怎么实现的吧。
初始化computed
function initComputed (vm: Component, computed: Object) {
const watchers = vm._computedWatchers = Object.create(null)
for (const key in computed) {
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef : userDef.get
// component-defined computed properties are already defined on the
// component prototype. We only need to define computed properties defined
// at instantiation here.
if (!(key in vm)) {
defineComputed(vm, key, userDef)
}
}
}
function initWatch (vm: Component, watch: Object) {
for (const key in watch) {
const handler = watch[key]
if (Array.isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i])
}
} else {
createWatcher(vm, key, handler)
}
}
}
🌈当初始化computed
时,为每一个属性创建一个Watcher
对象,通过Object.defineProperty
配置getter
,getter
运行过程中就会收集依赖。
⭐️但是计算属性的Watcher
不会立即执行,要看模板中是否有使用到该计算属性,也就是依赖该计算属性,如果模板中没有使用,Watcher
就不会去执行。由于,在给每个属性创建Watcher
实例的时候,Vue
配置了lazy
,可以让Watcher
不立即执行。
实现缓存
Watcher内部维护两个属性用来做缓存:value
和dirty
value
属性用于保存Watcher运行的结果,受lazy
的影响,value
在最开始是undefined
dirty
属性用于指示当前的value
是否为脏值,受lazy
的影响,该值在最开始是true
const computedWatcherOptions = { lazy: true }
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}
工作流程
⭐️当页面依赖计算属性时,vue首先检查其对应的Watcher
中的dirty
属性,判断该值是否是脏值,如果是,则运行getter
进行计算,并得到对应的值,保存在Watcher
的value
中,然后把Watcher
中的dirty
改为false
,然后返回。如果多次使用属性,也是先判断该值是否为脏值,如果不是脏值,直接读取value
值,做到数据缓存。
⭐️当计算属性的依赖数据变化时,就会先触发计算属性的Watcher
运行,把dirty
配置改为true
,不做任何处理。当组件渲染的时候,重新读取计算属性,dirty
为true
,因此会重新运行getter
进行计算、缓存。
⭐️扩展阅读: