Vue之计算属性
示例:
<template>
<div id="app">
<div ref="msg">{{ name }}</div>
<button @click="change">change</button>
<button @click="changeLast">changeLast</button>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
firstName: 'Yi',
lastName: 'Huang',
useless: 0
};
},
computed: {
name() {
if (this.useless > 0) {
return this.firstName + ',' + this.lastName;
}
return 'please click change'
}
},
methods: {
change() {
this.useless++;
},
changeLast() {
this.lastName = 'Zhang';
}
}
}
</script>
- 首先在App组件render生成vnode过程中,会访问name,访问name就会执行到以下代码:|
function createComputedGetter (key) { return function computedGetter () { const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { if (watcher.dirty) { watcher.evaluate() // 这一步Dep.target是这个computed watcher,完成了依赖收集,然后因为执行了popTarget,Dep.target 变回了渲染watcher } if (Dep.target) { watcher.depend() // 这一步将刚才收集的依赖又添加到了渲染watcher中 } return watcher.value } } }
- 这个函数中,首先执行watcher.evaluate,执行过程中会对(用户写入的computed里面的name方法进行求值),求值的过程中就会访问到this.useless的getter方法,执行getter方法进行依赖收集,此时订阅的依赖是由computed的watcher订阅的依赖,也就是computed的watcher去订阅this.useless的变化,当this.useless的值发生变化时,就会触发set之后执行dep.notify而执行computed watcher自身的update。
- 执行完后返回值:'please click change'。接着执行watcher.depend方法,这一步将刚刚computed收集到的依赖,又添加进渲染watcher中,这样当值发生变化时,就会同时触发computed的update和重新渲染。
- 当点击change按钮时,this.useless发生变化,触发set,调用dep.notify,遍历dep.subs中的两个wacher,一个是computed wathcer,一个是渲染wacher,依次执行update方法,如下
update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this) } }
- 第一次是把this.dirty置为true,因为它是计算属性的watcher。第二次直接执行queueWatcher进行渲染,在渲染的过程中又会进行重新求值,在求值的过程中又会触发get,收集新的依赖(包括firstName和lastName的依赖),作用是为了当他们两个的值发生变化时,会触发computed的update过程。最后执行页面的重新渲染。
在vue新版本中,只要修改了this.useless的值,不管计算属性最终返回什么值,都会进行重新渲染,而在旧版中会先判断值是否发生变化,如果计算属性返回的值没有变化,则不进行渲染。一句话说就是新版本:少计算,多渲染。旧版本:多计算,少渲染。