前言
本文是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 watcher
var 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 getter
if (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实现页面更新。