watch源码简化版
Watch实现原理
let vm = new Vue({
el: '#app',
data(){
return {name:'zf'}
},
watch:{
name(newValue,oldValue){
console.log(newValue,oldValue);
}
}
}
new Vue 发生了什么:
if (opts.watch) {
initWatch(vm,opts.watch);
}
initWatch:
function initWatch(vm, watch) {
for (const key in watch) {
const handler = watch[key];
// 如果结果值是数组循环创建watcher
if (Array.isArray(handler)) {
for (let i = 0; i < handler.length; i++) {
createWatcher(vm,key,handler[i]);
}
}else{
createWatcher(vm,key,handler)
}
}
}
function createWatcher(vm,exprOrFn,handler,options){
// 如果是对象则提取函数 和配置
if(isObject(handler)){
options = handler;
handler = handler.handler;
}
// 如果是字符串就是实例上的函数
if(typeof handler == 'string'){
handler = vm[handler];
}
return vm.$watch(exprOrFn,handler,options);
}
$watch:
stateMixin(Vue);
export function stateMixin(Vue) {
Vue.prototype.$watch = function(exprOrFn, cb, options = {}) {
options.user = true; // 标记为用户watcher
// 核心就是创建个watcher
const watcher = new Watcher(this, exprOrFn, cb, options);
if(options.immediate){
cb.call(vm,watcher.value)
}
}
}
Watcher:
class Watcher {
constructor(vm, exprOrFn, callback, options) {
// ...
this.user = !! options.user
if(typeof exprOrFn === 'function'){
this.getter = exprOrFn;
}else{
this.getter = function (){ // 将表达式转换成函数
let path = exprOrFn.split('.');
let obj = vm;
for(let i = 0; i < path.length;i++){
obj = obj[path[i]];
}
return obj;
}
}
this.value = this.get(); // 将初始值记录到value属性上
}
get() {
pushTarget(this); // 把用户定义的watcher存起来
const value = this.getter.call(this.vm); // 执行函数 (依赖收集)
popTarget(); // 移除watcher
return value;
}
run(){
let value = this.get(); // 获取新值
let oldValue = this.value; // 获取老值
this.value = value;
if(this.user){ // 如果是用户watcher 则调用用户传入的callback
this.callback.call(this.vm,value,oldValue)
}
}
}