为降低心智负担,本文只做原理和流程的分析,尽量不涉及Vue.js源码。
如果想要看结合源码的分析,建议猛戳这里:《Vue 的计算属性真的会缓存吗?(保姆级教学,原理深入揭秘)》。这篇是我看过的最好的Vue.js原理分析文章,没有之一。
作者写的所有Vue.js原理解析文章都非常好,建议大家都能阅读一下并关注作者(与我利益无关,与大家利益相关啊)
watcher系列文章在此,本篇是第二篇
computed属性值一般可以用在哪些地方呢?我们说两个常用简单例子
export default { template: '<div>My name is {{ fullname }}</div>', data () { return { firstName: 'Steve', lastName: 'Jobs' } }, computed: { fullName: ({ firstName, lastName }) => `${firstName}·${lastName}` } } 复制代码
export default { template: '<div>{{ selfIntroduction }}</div>', data () { return { firstName: 'Steve', lastName: 'Jobs' } }, computed: { fullName: ({ firstName, lastName }) => `${firstName}·${lastName}`, selfIntroduction: ({ fullName }) => `My name is ${fullName}` } } 复制代码
看过了上面的使用场景,我们应该提出的问题是:
经过了上节render-watcher的介绍,我们应该有了一个大概的思路:
firstName
和lastName
的订阅者队列里下面我们来看看怎么建立这个联系
首先要对computed属性进行初始化。
当完成所有data属性值的访问劫持劫持之后,便进入了computed属性的初始化流程:
其中get劫持是最关键的步骤。在这个环节里,实现了我们上面提出的解决方案:
这样就能实现,当data属性值变化后:
我们来看看具体是怎么实现的:
齿轮从render函数被执行开始转起。
render函数执行时,触发了computed属性值的get。
接着,在get里依序发生了这么几件事:
执行computed属性函数,将函数返回值保存起来
注意,执行computed函数的过程中,data属性值被get了。
data属性值把当前的computed属性的watcher添加到订阅队列里
data属性值把render-watcher添加到订阅队列里
返回保存起来的computed函数执行结果
注意订阅队列里的顺序,computed-watcher先,render-watcher后。
好了,齿轮的第一次运转结束了。
data的属性值被改变了。比如我们修改示例1中的lastName
属性:this.lastName = 'Nash'
好了,这个时候会发生什么?
没错,齿轮又一次运转了起来。
逻辑好的读者可能已经开始起疑了:
computed-watcher回调执行了一次computed函数,render-watcher回调render函数,会触发computed属性的get进而又执行了computed函数,这不是重复执行了两次?
说的对!这么干不行!我们只能挑一个watcher,在其回调里触发computed函数的执行。这么做:
抽出上面的核心逻辑,用伪代码表示一下:
// dirty就是数据该更新了 if (dirty) { setComputedValueCache(update()) dirty = false } return getComputedValueCache() 复制代码
一言蔽之,每次都返回缓存值。无非是返回前更不更新这个缓存值而已。
到这里,我们已经弄清楚,在模板里使用computed属性值时,是如何做到响应式的了;也弄清楚computed-watcher是什么、干什么用、怎么用了。
事已至此,顺手梳理一下上面示例2的情况,即:当computed属性引用了computed属性时,会发生什么?
什么是间接引用computed属性?看上面示例2就知道,引用链如下:
模板 → computed属性1 → computed属性2 → data属性值
computed属性2就是模板的间接引用。
经过上文,想必我们已经不必再从头开始分析了(肝不动了😂😂😂)。直接看data属性值最后的订阅队列:
data属性值→ [computed-watcher1, computed-watcher2, render-watcher]。
data属性值被set的时候,computed-watcher1、computed-watcher2相继被标记“待更新”;待到render-watcher更新时,就会依序执行:
至此,我们就可以对computed-watcher有了最基本、最简单的认识了。