内容简介:上一节着重讲述了在demo示例中,我们定义了一个计算属性。本章节我们继续探究这个计算属性的相关流程。
前言
上一节着重讲述了 initData
中的代码,以及数据是如何从data中到视图层的,以及data修改后如何作用于视图。这一节主要记录 initComputed
中的内容。
正文
前情回顾
在demo示例中,我们定义了一个计算属性。
computed:{ total(){ return this.a + this.b } }
本章节我们继续探究这个计算属性的相关流程。
initComputed
// initComputed(vm, opts.computed) function initComputed (vm: Component, computed: Object) { // 定义计算属性相关的watchers. const watchers = vm._computedWatchers = Object.create(null) // 是否是服务端渲染,这里赞不考虑。 const isSSR = isServerRendering() for (const key in computed) { // 获得用户定义的计算属性中的item,通常是一个方法 // 在示例程序中,仅有一个key为total的计算a+b的方法。 const userDef = computed[key] const getter = typeof userDef === 'function' ? userDef : userDef.get if (process.env.NODE_ENV !== 'production' && getter == null) { warn( `Getter is missing for computed property "${key}".`, vm ) } if (!isSSR) { // create internal watcher for the computed property. // 为计算属性创建一个内部的watcher。 // 其中computedWatcherOptions的值为lazy,意味着这个wacther内部的value,先不用计算。 // 只有在需要的情况下才计算,这里主要是在后期页面渲染中,生成虚拟dom的时候才会计算。 // 这时候new Watcher只是走一遍watcher的构造函数,其内部value由于 // lazy为true,先设置为了undefined.同时内部的dirty = lazy; watchers[key] = new Watcher( vm, getter || noop, noop, computedWatcherOptions // 上文定义过,值为{lazy: true} ) } // component-defined computed properties are already defined on the // component prototype. We only need to define computed properties defined // at instantiation here. // 组件定义的属性只是定义在了组件上,这里只是把它翻译到实例中。即当前的vm对象。 if (!(key in vm)) { // 将计算属性定义到实例中。 defineComputed(vm, key, userDef) } else if (process.env.NODE_ENV !== 'production') { if (key in vm.$data) { warn(`The computed property "${key}" is already defined in data.`, vm) } else if (vm.$options.props && key in vm.$options.props) { warn(`The computed property "${key}" is already defined as a prop.`, vm) } } } }
defineComputed
const sharedPropertyDefinition = { enumerable: true, configurable: true, get: noop, set: noop } // defineComputed(vm, key, userDef) export function defineComputed ( target: any, key: string, userDef: Object | Function ) { // 是否需要缓存。即非服务端渲染需要缓存。 // 由于本案例用的demo非服务端渲染,这里结果是true const shouldCache = !isServerRendering() if (typeof userDef === 'function') { // userDef = total() {...} sharedPropertyDefinition.get = shouldCache // 根据key创建计算属性的getter ? createComputedGetter(key) : userDef // 计算属性是只读的,所以设置setter为noop. sharedPropertyDefinition.set = noop } else { sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache !== false ? createComputedGetter(key) : userDef.get : noop sharedPropertyDefinition.set = userDef.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 ) } } // 将组件属性-》实例属性,关键的一句,设置属性描述符 Object.defineProperty(target, key, sharedPropertyDefinition) }
createComputedGetter
// 根据key创建计算属性的getter // createComputedGetter(key) function createComputedGetter (key) { return function computedGetter () { // 非服务端渲染的时候,在上述的initComputed中定义了vm._computedWatchers = {},并根据组件中的设定watchers[key] = new Watcher(..),这里只是根据key取出了当时new的watcher const watcher = this._computedWatchers && this._computedWatchers[key] if (watcher) { // watcher.dirty表示这个值是脏值,过期了。所以需要重新计算。 // new Watcher的时候,这个total的watcher中,内部的dirty已经被置为 // dirty = lazy = true; // 那么这个值什么时候会过期,会脏呢。就是内部的依赖更新时候, // 比如我们的total依赖于this.a,this.b,当着两个值任意一个变化时候 // 我们的total就已经脏了。需要根据最新的a,b计算。 if (watcher.dirty) { // 计算watcher中的值,即value属性. watcher.evaluate() } // 将依赖添加到watcher中。 if (Dep.target) { watcher.depend() } // getter的结果就是返回getter中的值。 return watcher.value } } }
initComputed小结
继initComputed之后,所有组件中的computed都被赋值到了vm实例的属性上,并设置好了getter和setter。在非服务端渲染的情况下,getter会缓存计算结果。并在需要的时候,才计算。setter则是一个什么都不做的函数,预示着计算属性只能被get,不能被set。即只读的。
接下来的问题就是:
{lazy:true}
接下来我们继续剖析后面的代码。
用来生成vnode的render函数
下次再见到这个计算属性total的时候,已是在根据el选项或者template模板中,生成的render函数,render函数上一小节也提到过。长这个样子。
(function anonymous() { with (this) { return _c('div', { attrs: { "id": "demo" } }, [_c('div', [_c('p', [_v("a:" + _s(a))]), _v(" "), _c('p', [_v("b: " + _s(b))]), _v(" "), _c('p', [_v("a+b: " + _s(total))]), _v(" "), _c('button', { on: { "click": addA } }, [_v("a+1")])])]) } } )
这里可以结合一下我们的html,看出一些特点。
<div id="demo"> <div> <p>a:{{a}}</p> <p>b: {{b}}</p> <p>a+b: {{total}}</p> <button @click="addA">a+1</button> </div> </div>
这里使用到计算属性的主要是这一句
_v("a+b: " + _s(total))
那么对于我们来说的关键就是 _s(total)
。由于这个函数的 with(this)
中, this
被设置为vm实例,所以这里就可以理解为 _s(vm.total)
。那么这里就会触发之前定义的 sharedPropertyDefinition.get
-> initComputed() -> defineComputed() -> Object.defineProperty(target, key, sharedPropertyDefinition)
也就是如下的内容:
coding...
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Node.js开发指南
郭家寶(BYVoid) / 人民邮电出版社 / 2012-7 / 45.00元
Node.js是一种方兴未艾的新技术,诞生于2009年。经过两年的快速变化,Node.js生态圈已经逐渐走向稳定。Node.js采用了以往类似语言和框架中非常罕见的技术,总结为关键词就是:非阻塞式控制流、异步I/O、单线程消息循环。不少开发者在入门时总要经历一个痛苦的思维转变过程,给学习带来巨大的障碍。 而本书的目的就是帮助读者扫清这些障碍,学会使用Node.js进行Web后端开发,同时掌握事件驱......一起来看看 《Node.js开发指南》 这本书的介绍吧!
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
HEX HSV 转换工具
HEX HSV 互换工具