Vue MVVM模型原理
最近反思了下自己,觉得自己很急躁,学技术总是觉得能用就行了,其实这样很不好,总是这样,就永远只能当用轮子的人。好了,废话不多说,转入正题:
要理解MVVM的原理,首先要理解它是什么,怎么运作起来的:
以下这样图来自这位大佬的文章《Vue.js入门(一)--MVVM框架理解》
由图可见,MVVM模型需要靠Observer(监视者)、Compile(解析器)、Dep(Dependency,收集依赖)、Watcher(观察者)等来实现。
Observer()之所以能够监听数据变化,是因为它依靠了es5的Object.defineProperty(obj, prop, descriptor):
具体关于Object.defineProperty()的属性,可见MDN的Object.defineProperty()。
这个方法可以给对象添加或一个属性,并且返回这个对象,它有三个参数,前面两个obj、prop不多说了,重头戏是第三个参数descriptor,既描述对象,这个对象可以传configurable、enumerable、value...以及get、set
function Observer(obj, vm) { Object.keys(obj).forEach(function(key) { Object.defineProperty(obj, key, { get: function() { return obj[key]; }, set: function(newVal) { if (newVal === obj[key]) return; obj[key] = newVal; } }); }); }
这段代码的作用就是遍历obj的每个属性,加上get和set,读取obj属性的时候就会触发get,给obj属性赋值的时候就会触发set.
那Observer()怎么样才能在数据变更时及时通知到Dep,再有Dep去通知Watcher呢?看看Dep()代码:
1 /** 2 * 收集Watcher依赖并通知数据变更 3 */ 4 function Dep () { 5 this.subs = [] 6 } 7 Dep.prototype = { 8 9 // 添加订阅者 10 addSub: function(sub) { 11 this.subs.push(sub); 12 },
13 // 通知订阅者 14 notify: function() { 15 this.subs.forEach(function(sub) { 16 sub.update(); 17 }); 18 } 19 };
改写下Observer():
1 function observe (obj, vm) { 2 Object.keys(obj).forEach(function (key) { 3 defineReactive(vm, key, obj[key]); 4 }); 5 } 6 function defineReactive (obj, key, val) { 7 var dep = new Dep(); 8 Object.defineProperty(obj, key, { 9 get: function () { 10 if (Dep.target) dep.addSub(Dep.target); 11 return val 12 }, 13 set: function (newVal) { 14 if (newVal === val) return 15 val = newVal; 16 dep.notify(); 17 } 18 }); 19 }
而Dep.target指的是Watcher:
1 function Watcher(vm, cb, expOrFn) { 2 // this为Watcher构造函数 3 Dep.target = this; 4 this.cb = cb 5 this.vm = vm 6 this.expOrFn = expOrFn 7 this.value = this.get() 8 this.update(); 9 Dep.target = null; 10 } 11 Watcher.prototype = { 12 update: function () { 13 this.run() 14 }, 15 run: function () { 16 const value = this.get() 17 if (value !== this.value) { 18 this.value = value 19 this.cb.call(this.vm) 20 } 21 }, 22 23 // 获取属性值 24 get: function () { 25 this.value = this.vm[this.expOrFn]; 26 } 27 }
由此可见:
Oberver()通过Object.defineProperty()通知Dep(),而Dep()这里收集了Watcher依赖,当Dep()里有个订阅者的数组,全都是Watcher,当Oberver触发了Dep原型上的方法notify()的时候,就会触发Watcher去update,更新数据,而view层将进行render(),从而更新视图。