- 我们知道,计算属性在触发get的时候会收集依赖
- 对于计算属性的函数调用?依赖又是怎么收集的,以及细节处理?
initComputed -> 创建Watch响应式
//-> 设置其getter:Object.defineProperty(target, key, sharedPropertyDefinition);
// 用于访问该属性时,触发的动作
// 代码如下
function createComputedGetter (key) {
return function computedGetter () {
var watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
// 如果依赖改变了,从新求值
// 相当于方法,这里会惰性求值
if (watcher.dirty) {
watcher.evaluate();
}
// 用于收集依赖
if (Dep.target) {
watcher.depend();
}
return watcher.value
}
}
}
1、对于问题1:computed属性,具有惰性求值的优势
2、 对于模板中使用到的属性方法等,Vue其实也是会收集其依赖,一旦依赖改变,就会触发_update
// 访问this.$data 找到对应的get
set otherMsg: ƒ reactiveSetter(newVal)
arguments: (...)
caller: (...)
length: 1
name: "reactiveSetter"
prototype: {constructor: ƒ}
__proto__: ƒ ()
[[FunctionLocation]]: vue.runtime.esm.js?2b0e:1037
[[Scopes]]: Scopes[4]
---------------------------
然后执行 dep.notify();
Dep.prototype.notify = function notify () {
// stabilize the subscriber list first
var subs = this.subs.slice();
if (process.env.NODE_ENV !== 'production' && !config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort(function (a, b) { return a.id - b.id; });
}
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
};
------------------------
// 以下时打印观察者subs其中一项的内容
active: true
before: ƒ before()
cb: ƒ noop(a, b, c)
deep: false
depIds: Set(11) {6, 7, 8, 12, 16, …}
deps: (11) [Dep, Dep, Dep, Dep, Dep, Dep, Dep, Dep, Dep, Dep, Dep]
dirty: false
expression: "function () {↵ vm._update(vm._render(), hydrating);↵ }"
getter: ƒ ()
id: 2
lazy: false
newDepIds: Set(0) {}
newDeps: []
sync: false
user: false
value: undefined
vm: VueComponent {_uid: 1, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
__proto__: Object
// 由此可知
模板中的变量,在渲染的时候,其实也是会去收集依赖,然后一旦变量改变,就会触发
function () {↵ vm._update(vm._render(), hydrating);↵ }
// 我们通过这个也可以用于调试看某个变量是否会触发视图的更新
对于问题2:如果我们在模板中
{{ computedMsg(item) }}在v-for中这个场景还是比较常见的
<template>
<ul>
<li v-for="(item,index) in list" :key="index">
{{ computedMsg(item) }}
</li>
</ul>
</template>
<script>
export default {
name:'computedTest',
data(){
return {
list:[
{name:'张三',age:20,job:'it'},
{name:'李四',age:21,job:'teacher'}
],
otherMsg:'xx村大学生就业统计'
}
},
computed:{
computedMsg(){
return (item) => {
return `${this.otherMsg}:${item.name}-${item.age}-${item.job}`
}
}
},
created(){
setTimeout(() => {
this.otherMsg = '2020下半年'
}, 2000);
}
}
</script>
// 1、执行evaluate/get 执行的内容的return,内部函数此时并不会执行,此时是怎么收集到依赖的?
// 答:因为闭包,其实是使用到了外部函数的this的。但没有收集到this.otherMsg
// 真正收集到这个依赖的,其实是在模板渲染的时候收集到的 {{ computedMsg(item) }},具体看上段模板渲染有说明
// 其中上面模板渲染的ubs的一项其实就是this.otherMsg的。也就是thisMsg的变化,为什么会触发视图更新呢,因为模板渲染中,执行了计算属性的内部函数,此时收集依赖到视图更新了
- 总结:
- 了解了计算属性,模板渲染过程中的依赖是怎么收集的
- 深入分析了计算属性如果是函数调用,依赖收集又是怎么进行的
- 建议:
1、如果能缓存一些计算值,建议用计算属性,把能缓存的计算,放在外部函数执行,内部函数执行其它的
2、如果不能缓存一些属性,有上可知,效率和绑定方法是一样的。建议用函数,会更明了