往期
前言
本文分为入门和进阶两部分,建议有经验的读者直接阅读进阶部分。
本文主要参考了vue这一开源库,若读者阅读过它的源码可以直接跳过本文 :)
入门
关于访问对象的某个属性
既然是入门,还是先提一下Vue.prototype.$watch
的几种用法
const vm = Vue({
data() {
return {
b: true,
o: { name: 'obj' },
a: ['a', 'b', 'c'],
odeep: {
path: {
name: 'obj deep',
value: [],
},
},
};
},
watch: {
// 如果b的值改变了,打印改变前与改变后的值
b(val, oldVal) {
console.warn(val, oldVal);
},
// 如果o.name的值改变了,打印改变前与改变后的值
'o.name': {
handler(val, oldVal) {
console.warn(val, oldVal);
},
},
},
created() {
// 深度监听: 如果odeep.path.name/odeep.path.value的值改变了,打印odeep.path改变前与改变后的值
this.$watch('odeep.path', (val, oldVal) => {
console.warn(val, oldVal);
}, { deep: true });
},
});
复制代码
如何去通过诸如o.name
的字符串访问到vm.o.name
呢? vm['o.name']
当然是不行的,需要写成vm['o']['name']
这样的形式。
function parsePath(path) {
if (!/[\w.]$/.test(path)) {
// 为什么要返回一个带参数的函数呢? 提前告诉你,是为了触发被监听对象的get方法(还记得上一篇文章的内容吗)
return function(obj) {};
}
const segs = path.split('.');
// 想知道这里为什么不用forEach吗,试试在forEach里使用return吧
return function(obj) {
for (let i = 0; i < segs.length; i += 1) {
if (!obj) {
return;
}
obj = obj[segs[i]];
}
return obj;
};
}
const obj = {
o: { name: 'a' },
};
console.assert(parsePath('o.name')(obj) === 'a');
复制代码
关于观察者模式
先让我们看看维基百科是怎么说的:
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
也就是说subject用来维护依赖列表, 每个依赖都是一个observer。当依赖列表中的某一项发生了变化,就自动通知subject自身状态的变更。
让我们先拷贝一下上篇文章的内容, 注意注释里的内容!
function defineReactive(obj, key, val) {
if (isPlainObject(val)) {
observe(val);
} else if (Array.isArray(val)) {
dealAugment(val, dep);
observeArray(val);
}
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
// 将依赖加入依赖列表
return val;
},
set(newVal) {
if (val !== newVal) {
val = newVal;
if (isPlainObject(newVal)) {
observe(newVal);
} else if (Array.isArray(newVal)) {
dealAugment(newVal, dep);
observeArray(newVal);
}
// 依赖通知subject自身状态的改变,即调用callback
}
},
});
}
复制代码
但是callback在$watch
函数中,如何传递给依赖, 并在被监听对象该属性变化时调用呢?
我们可以利用一个全局变量(在这里我们称它为DepTarget),在访问变量的时候设置为$watch
函数的callback, 并将这个callback存到一个集合里,访问结束后置空。同时需要注意的是,