前言
watch的实现原理同样是利用了watcher。不同的是:渲染watch在数据更新时去更新dom元素,而watch在数据更新时执行相对应的操作。
watch实现原理
1.先获取传入的watch,并通过createWatcher创建
function initWatch(vm) {
let watch = vm.$options.watch
for(let key in watch) {
const handler = watch[key]; // handler可能是数组、字符串、对象、函数
if (Array.isArray(handler)) {
handler.forEach(handler => {
createWatcher(vm,key,handler)
})
} else {
createWatcher(vm,key,handler);
}
}
}
2.在createWatcher中把handler函数提取处理,成为最终数据更新后的回调函数。
export function createWatcher(vm,exprOrFn,handler,options={}) {// options用来表示用户传入
if (typeof handler == 'object') {
options = handler
handler = handler.handler
} else if (typeof handler == 'string') {
handler = vm[handler]
}
return vm.$watch(exprOrFn,handler,options)
}
3.创建一个Watcher实例,实现依赖收集和依赖更新操作。
export function stateMixin(Vue) {
Vue.prototype.$nextTick = function (cb) {
nextTick(cb)
}
Vue.prototype.$watch = function (exprOrFn,cb,options) {
// 数据更新后应该让watcher重新执行
let watcher = new Watcher(this,exprOrFn,cb,{...options,user:true})
if (options.immediate) {
cb() // 如果是immediate应该立刻执行
}
}
}
4.在watcher里,很重要的一步是获取需要监听的key值,通过getter函数可以获取到监听的值。
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;
}
}
5.在函数run中,如果是渲染watcher,只要调用get函数实现渲染就可以了。如果包含watch,则还需要调用用户传入的回调函数。
run() {
let newValue = this.get() // 渲染
let oldValue = this.value
this.value = newValue // 更新老值
if (this.user) {
this.cb.call(this.vm,newValue,oldValue)
};
}