class Vue {
constructor(options) {
this.options = options;
this.$data = options.data;
this.initData(this.$data); // 把data代理到vue实例上
if (this.options.watch) this.initWatch(this.options.watch);
}
initData(data) {
Object.keys(data).forEach(key => {
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get() {
return data[key];
},
set(newVal) {
if (newVal === data[key]) return;
data[key] = newVal;
}
})
})
observe(data);
}
initWatch(watch) { // 初始化vue实例时,获取watch对象,并实例化每个watch
Object.keys(watch).forEach(key => {
new Watcher(this, key, watch[key])
})
}
}
function observe(data) {
const type = Object.prototype.toString.call(data);
if (type !== ('[object Object]' || '[object Array]')) return; // 非引用类型的不用再深度响应式
new Observer(data);
}
class Observer {
constructor(data) {
this.walk(data);
}
walk(data) {
Object.keys(data).forEach(key => defineReactive(data, key, data[key]))
}
}
function defineReactive(data, key, value) {
observe(value); // 如果依然是引用类型,再次深度响应式
const dep = new Dep();
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get() {
dep.depend(); // 触发响应式get方法,执行dep实例的depend方法,将对应的watch回调存入dep实例的subs数组中
return value;
},
set(newVal) {
if (newVal === value) return false;
dep.notify(value, newVal); // 每次设置属性后,调用dep实例的notify方法
value = newVal;
}
})
}
class Dep {
constructor() {
this.subs = [];
}
depend() {
if (Dep.target) {
this.subs.push(Dep.target);
}
}
notify(value, newVal) { // 将当前vue实例身上某个属性所对应的属性的所有watch回调遍历并执行
this.subs.forEach(watch => {
watch.run(value, newVal); // 每个callback在watcher实例的run方法中执行
})
}
}
class Watcher {
constructor(vm, exp, callback) {
this.vm = vm; // 保存每个watch对应的vue实例
this.exp = exp; // 保存每个watch监听的vue实例上的属性
this.callback = callback; // 保存每个属性所对应的回调
this.get(); // 收集回调到dep实例
}
get() {
Dep.target = this; // 将Dep类的target静态属性指向当前Watcher实例
this.vm[this.exp]; // 手动获取vue实例上的对应属性,并且触发响应式get方法
Dep.target = null; // 将watch回调存入dep实例的subs数组中后,销毁Dep.target属性,以便下次实例化Watcher时再次使用
}
run(value, newVal) {
Promise.resolve().then(() => { // 此处使用微队列,否则在watch回调中获取的对应的属性还是改变之前的,因为赋值操作逻辑在回调后面执行
this.callback.call(this.vm, value, newVal); // 将watch回调绑定到vue实例,以便可以在watch回调中使用this,指向vue实例
})
}
}
2. Vue2 响应式原理-watch原理
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- 1、 数据响应式 首先请大家认真的思考一个问题:什么是数据响应式? 答:数据变化是可侦测的,并且和数据相关的内容可...
- 本文源码版本 Vue3.2.11,Vue2 响应式源码剖析点这里 深入浅出 Vue2 响应式原理源码剖析[http...
- Vue.js: 渐进式JavaScript框架官方文档点我直达[https://cn.vuejs.org/] Vu...