Appearance
八、watch的原理
0、使用方式
js
// 第一种
let vm = new Vue();
vm.$watch(function (newVal, oldVal) {
console.log(newVal, oldVal);
});
vm.$watch('a.b.c', function (newVal, oldVal) {
console.log(newVal, oldVal);
})
// 第二种
let vm = new Vue({
el: '#app',
data: {
a: {a: {a: 1}},
b: 2
},
methods: {
cc() {
console.log('method cc');
}
},
watch: {
// 1. key value
'a.a.a': {
handler(newValue, oldValue) {
// 对象没有老值,都是新
console.log(newValue, oldValue);
},
immediate: true // 可选
},
// 2. 写成key和数组的方式
'b': [
(newValue, oldValue) => {
console.log(newValue);
},
(newValue, oldValue) => {
console.log(newValue);
}
],
// 3、监控当前实例上的方法
'c': 'cc',
// 4、handler的写法
'd': {
handler() {
console.log('ddd');
}
}
}
});1、扩展$watch方法
js
// src\state.js
export function initState(vm) {
const opts = vm.$options
// ......
if (opts.watch) {
initWatch(vm)
}
}
function initProps(vm) {}
function initMethods(vm) {}
// 初始化数据
function initData(vm) {}
function initComputed(vm) {}
// 初始化watch
function initWatch(vm) {
let watch = vm.$options.watch;
for (let key in watch) {
// handler 可能是数组、字符串、对象、函数
const handler = watch[key];
if (Array.isArray(handler)) {
// 数组
handler.forEach(handle => {
createWatcher(vm, key, handle);
})
} else {
// 字符串、对象、函数
createWatcher(vm, key, handler);
}
}
}
// 创建watcher:options 用来标识是用户watcher
function createWatcher(vm, exprOrFn, handler, options) {
if (typeof handler == 'object') {
options = handler;
handler = handler.handler; // 是一个函数
}
if (typeof handler == 'string') {
handler = vm[handler]; // 将实例的方法作为handler
}
// key handler 用户传入的选项
return vm.$watch(exprOrFn, handler, options);
}
export function stateMixin(Vue) {
Vue.prototype.$nextTick = function(cb) {
nextTick(cb);
}
// 在Vue的原型上面挂载$watch方法
Vue.prototype.$watch = function(exprOrFn, cb, options = {}) {
// 数据应该依赖这个watcher,数据变化后应该让watcher从新执行,user表示为自定义的watcher
let watcher = new Watcher(this, exprOrFn, cb, {...options, user: true });
if (options.immediate) {
// 如果是immediate,立刻执行更新
cb();
}
}
}2、watcher类的getter方法改写
js
// src\observer\watcher.js
class Watcher {
constructor(vm, exprOrFn, cb, options) {
// .....
this.user = options.user; // 用户watcher
this.isWatcher = typeof options === 'boolean'; // 标识是渲染watcher
if (typeof exprOrFn == 'function') {
this.getter = exprOrFn;
} else {
// exprOrFn 可能传递过来的是一个字符串a
this.getter = function() {
// 当去当前实例上取值时,才会触发依赖收集
let path = exprOrFn.split('.'); // ['a', 'a', 'a']
let obj = vm;
for (let i = 0; i < path.length; i++) {
obj = obj[path[i]]; // vm.a.a.a
}
return obj;
}
}
// 默认会先调用一次get方法,进行取值,将结果保留下来
this.value = this.get();
}
addDep(dep) {}
get() {
// 当前watcher实例
pushTarget(this);
// 调用exprOrFn,渲染页面 > 取值(执行了get方法)
let result = this.getter();
// 渲染完成后,将watcher删掉
popTarget();
return result;
}
run() {
// 渲染逻辑
let newValue = this.get();
let oldValue = this.value;
this.value = newValue; // 更新老值
if (this.user) {
// 调用cb方法
this.cb.call(this.vm, newValue, oldValue);
}
}
update() {}
}
export default Watcher;