一、Vue源码解析(一)-模版渲染
二、Vue源码解析(二)-MVVM双向绑定vue
官网给出的demo以下segmentfault
<div id="app"></div> new Vue({ el: '#app', template: `<div> <p>Original message is: {{ message }}</p> <p>Computed reversed message:: {{ reversedMessage }}</p> </div>`, data(){ return { message: 'Hello', } }, computed:{ reversedMessage(){ return this.message.split('').reverse().join('') } } }) 结果: Original message: "Hello" Computed reversed message: "olleH"
//判断参数是否包含computed属性 if (opts.computed) { initComputed(vm, opts.computed); } function initComputed (vm, computed) { var watchers = vm._computedWatchers = Object.create(null); //本例中key=‘reversedMessage’ for (var key in computed) { //本例中userDef和getter是reversedMessage函数 var userDef = computed[key]; var getter = typeof userDef === 'function' ? userDef : userDef.get; //监听计算属性,设置lazy=true,延迟执行watcher的get方法 watchers[key] = new Watcher(vm,getter,{lazy:true}); //设置能够经过vm[key](本例vm.reversedMessage)方式访问计算属性 defineComputed(vm, key, userDef); } }
一、vue对象初始化时会针对computed属性的全部key值分别new一个watcher对象,在Vue源码解析(二)中有详细介绍watcher的原理,当时提到watcher初始化会当即调用一次watcher.get方法,而后实际上能够经过传入{lazy:true}参数来延迟watcher.get方法的执行缓存
var Watcher = function Watcher (vm,expOrFn,options){ //延迟计算 this.lazy = options.lazy; //尚未计算,因此数据是脏的 this.dirty = options.lazy; this.value = this.lazy ? undefined //计算getter值和收集依赖 : this.get(); }
二、defineComputed(vm, key, userDef),将computed属性代理到vm上,经过vm[key]访问computed属性值app
function defineComputed (target,key,userDef){ //userDef是function,getter设为userDef或userDef的值 if (typeof userDef === 'function') { //shouldCache是否缓存,这也是使用computed属性最重要的缘由,computed值会被缓存起来,而不是每次从新执行函数生成 sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : userDef; sharedPropertyDefinition.set = null; //userDef是否是function,getter设为userDef.get,setter设为userDef.set } else { sharedPropertyDefinition.get = userDef.get ? shouldCache && userDef.cache !== false ? createComputedGetter(key) : userDef.get : null; sharedPropertyDefinition.set = userDef.set ? userDef.set : null; } //,将computed属性代理到vm上,经过vm[key]访问computed属性值 Object.defineProperty(target, key, sharedPropertyDefinition); } function createComputedGetter (key) { return function computedGetter () { //shouldCache = true时直接返回缓存值watcher.value var watcher = this._computedWatchers && this._computedWatchers[key]; //存在脏数据则从新计算watcher的值 if (watcher.dirty) { watcher.evaluate(); } //直接返回缓存中watcher的值 return watcher.value } } }
三、前面提到watcher.get方法会延迟执行,那么到底啥时执行呢?这又得提到Vue源码解析(二)中的updateComponent方法,因为本例引用了计算属性{{ reversedMessage }},updateComponent中的render函数则会调用vm.reversedMessage,所以触发第二步的sharedPropertyDefinition.get函数,调用 watcher.evaluate(),最终调用watcher.get()来计算watcher的值和收集依赖。(watcher.get方法将监听vm.reversedMessage的watcher对象和发布vm.message变化的dep对象绑定,所以当vm.message变化时,vm.reversedMessage值也会同步变化)
所以watcher.get是在第一次访问vm.reversedMessage对象时调用的,因此若是模版没有用到{{ reversedMessage }}值的话vm.reversedMessage的值是不会被计算的函数
/** * Evaluate the value of the watcher. * This only gets called for lazy watchers. */ Watcher.prototype.evaluate = function evaluate () { this.value = this.get(); this.dirty = false; };
正好以前看到过一个问题vue.js使用computed计算某个属性后,该属性的双向绑定没了,看了本文的源码后你们应该了解了计算属性用在v-model上应该设置setter方法,例如本例中demo应该这么写:源码分析
new Vue({ el: '#app', template: `<div> <input v-model="reversedMessage" placeholder="edit me"> <p>Original message is: {{ message }}</p> <p>Computed reversed message:: {{ reversedMessage }}</p> </div>`, data(){ return { message: 'jixiangwu', } }, computed:{ reversedMessage:{ get(){ return this.message.split('').reverse().join('') }, set(val){ this.message = val.split('').reverse().join('') } } } })