前言
本文是vue2.x源码分析的第十篇,主要看computed和watch的处理过程!
实例代码
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Vue</title><script src="./vue10.js" type="text/javascript" charset="utf-8" ></script></head><body><div id="app">{{messages}}</div><script>var vm=new Vue({el:'#app',name:'app',data:{message:'message'},computed:{messages(){return this.message+'s'}},watch:{message(){alert('message changed!')}}});debugger;setTimeout(()=>{vm.message='messages'},0)</script></body></html>
1 computed
关键断点
initState(vm);
if (opts.computed) { initComputed(vm, opts.computed); }
watchers[key] = new Watcher(vm, getter, noop,
computedWatcherOptions);//option中lazy:true导致watcher.get不执行,从而该watcher不收集dep,dep也不订阅该watcher
defineComputed(vm, key, userDef);
看下defineComputed
function defineComputed (target, key, userDef) {if (typeof userDef === 'function') {sharedPropertyDefinition.get = createComputedGetter(key);sharedPropertyDefinition.set = noop;} else {sharedPropertyDefinition.get = userDef.get? userDef.cache !== false? createComputedGetter(key): userDef.get: noop;sharedPropertyDefinition.set = userDef.set? userDef.set: noop;}Object.defineProperty(target, key, sharedPropertyDefinition);}
一句话总结computed原理:通过Object.defineProperty将计算属性的key(即messages)定义为vm的存取器属性,该key的get函数即是计算属性的value(即messages对应的函数),key的更新随着它所依赖的data中的某个属性一同更新
2 watch
关键断点
initState(vm);
if (opts.watch) { initWatch(vm, opts.watch); }
createWatcher(vm, key, handler);
vm.$watch(key, handler, options);
看下该函数:
Vue.prototype.$watch = function (expOrFn,cb, options) {var vm = this;options = options || {};options.user = true; //user watchervar watcher = new Watcher(vm, expOrFn, cb, options);if (options.immediate) {cb.call(vm, watcher.value);}return function unwatchFn () {watcher.teardown();}};
var Watcher = function Watcher (vm,expOrFn,cb,options) {...this.user=true;this.cb = cb;...// parse expression for getterif (typeof expOrFn === 'function') { render watcher从这儿进this.getter = expOrFn;} else {this.getter = parsePath(expOrFn); user watcher从这儿进}this.value = this.lazy? undefined: this.get();};
接下来和render watcher一样执行get函数,然后message的dep订阅该user watcher,随后message的dep又订阅render watcher,也就是说message的dep.sub数组中有两个watcher,分别是user watcher和render watcher,页面初次渲染时user watcher不起什么作用,当message值改变导致页面更新时,先执行user watcher,这会执行该watcher.cb函数也即定义watch时的message的回调函数,从而实现了watch监控message变化的功能,然后执行render watcher实现页面更新。
