内容简介:计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。注意,如果某个依赖 (比如非响应式属性) 在该实例范畴之外,则计算属性是不会被更新的。上面这几段话其实可以归纳为以下几点:同以往一样,先新建一个
官方定义
{ [key: string]: Function | { get: Function, set: Function } }
计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。注意,如果某个依赖 (比如非响应式属性) 在该实例范畴之外,则计算属性是不会被更新的。
上面这几段话其实可以归纳为以下几点:
-
computed
是计算属性,会被混入到Vue
实例中 -
computed
的结果会被 缓存 ,除非依赖的响应式属性变化才会 重新计算
如何初始化 computed
?
同以往一样,先新建一个 Vue
项目,同时加入以下代码:
export default { name: 'test', data () { return { app: 666 } }, created () { console.log('app proxy -->', this.appProxy) }, computed () { appProxy () { debugger return this.app } } }
F12
打开调试界面,刷新后断点停在了 debugger
的位置,同时可以看到右边的调用栈:
appProxy get evaluate computedGetter created
瞥到 computedGetter
之后,点进去,可以看到:
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 } } }
看到这里不禁一脸懵逼:grimacing:
当然,根据前面我们看源码的经验,没有思路时,直接搜索相关函数的调用位置,这里我们可以直接搜索 createComputedGetter
,看看它是在哪里调用的。此处忽略搜索的过程,直接给出我的结论:
Vue
中存在两种初始化 computed
的方法:
option Vue.prototype.extend
这两种初始化其实大同小异,我们选择在组件中写 computed
,自然断点就会跑到 Vue.prototype.extend
函数里:
... if (Sub.options.computed) { initComputed$1(Sub); } ...
initComputed$1
函数:
function initComputed$1 (Comp) { // 拿到组件的computed var computed = Comp.options.computed; for (var key in computed) { // 循环遍历 defineComputed(Comp.prototype, key, computed[key]); } }
显然,这句代码: defineComputed(Comp.prototype, key, computed[key])
将 computed
挂载在了组件的原型上,下面来看下它的实现方式:
defineComputed
:
function defineComputed ( target, key, userDef ) { // 判断是否要将结果缓存下来 var shouldCache = !isServerRendering(); // 下面进行分类判断 // 对应的computed是函数的情况 if (typeof userDef === 'function') { sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : createGetterInvoker(userDef); sharedPropertyDefinition.set = noop; } else { // 非函数的情况 sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache !== false ? createComputedGetter(key) : createGetterInvoker(userDef.get) : noop; sharedPropertyDefinition.set = userDef.set || noop; } if (process.env.NODE_ENV !== 'production' && sharedPropertyDefinition.set === noop) { sharedPropertyDefinition.set = function () { warn( ("Computed property \"" + key + "\" was assigned to but it has no setter."), this ); }; } // 将sharedPropertyDefinition绑定到组件对象上 Object.defineProperty(target, key, sharedPropertyDefinition); }
:sweat_smile:感觉有点乱,最后再梳理下上边的逻辑:
initComputed
:
-
执行
initComputed
,从Vue
中拿到computed
对象里所有的key
值 -
循环拿到的
key
值,调用defineComputed
函数,把computed
绑定到组件对象上
defineComputed
:
-
判断是否在服务端渲染,是则
computed
的结果会被缓存,不是则不会缓存计算结果 -
由于
computed
存在两种写法,这里也对 函数 跟 对象 的写法做了区分
computed
的结果缓存是如何实现的?
上面我们大致梳理了下 computed
的初始化逻辑,现在我们回过头来再看一下官方定义,发现其中提到了 计算属性会将计算结果缓存下来
,那么这个计算结果到底是怎么被缓存下来的呢?
回到 defineComputed
defineComputed
里最后将 sharedPropertyDefinition
绑定到组件对象上,在代码里面可以看到对 sharedPropertyDefinition.get
做了特殊处理,两种情况分别封装了:
createComputedGetter createGetterInvoker
createComputedGetter
的实现:
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 } } }
createGetterInvoker
的实现:
function createGetterInvoker(fn) { return function computedGetter () { return fn.call(this, this) } }
可以看到,服务端渲染确实是对计算属性的结果不做缓存的,但是我们对结果是如何缓存,依旧是一脸懵逼:neutral_face:
回到最初的断点
刷新页面回到一开始我们在 appProxy
中打下的断点,在调用栈中有两个显眼的函数:
evaluate get
分别点进去,我们可以看到:
evaluate
实现源码:
Watcher.prototype.evaluate = function evaluate () { this.value = this.get(); this.dirty = false; };
get
实现源码:
Watcher.prototype.get = function get () { pushTarget(this); var value; var vm = this.vm; try { value = this.getter.call(vm, vm); } catch (e) { if (this.user) { handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\"")); } else { throw e } } finally { // "touch" every property so they are all tracked as // dependencies for deep watching if (this.deep) { traverse(value); } popTarget(); this.cleanupDeps(); } return value };
结合上面给出的 createComputedGetter
源码我们可以知道, computed
的计算结果是通过 Watcher.prototype.get
来得到的,拿到 value
以后,在 Wathcer.prototype.evaluate
中执行了这样一行代码:
... this.dirty = false;
聪明的读者肯定猜到了,计算属性是否重新计算结果,肯定跟这个属性有关。接下来我们只要跟踪这个属性的变化,就可以轻松的知道计算属性的缓存原理了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Golang源码探索(一) 编译和调试源码
- 使用gdb调试工具上手调试php和swoole源码
- JVM源码分析-JVM源码编译与调试
- 调试 Flink 源码
- Node.js 源码调试
- 如何断点调试Tomcat源码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Python 3网络爬虫开发实战
崔庆才 / 人民邮电出版社 / 2018-4 / 99
本书介绍了如何利用Python 3开发网络爬虫,书中首先介绍了环境配置和基础知识,然后讨论了urllib、requests、正则表达式、Beautiful Soup、XPath、pyquery、数据存储、Ajax数据爬取等内容,接着通过多个案例介绍了不同场景下如何实现数据爬取,后介绍了pyspider框架、Scrapy框架和分布式爬虫。 本书适合Python程序员阅读。一起来看看 《Python 3网络爬虫开发实战》 这本书的介绍吧!