整理vue框架的原理解析知识点——响应式问题,我们知道vue框架是基于MVVM模式,所谓的MVVM模式如下图所示:
所谓的MVVM是指由把Controller的数据和逻辑处理部分从中抽离出来,用一个专门的对象去管理,这个对象就是ViewModel,是Model和Controller之间的一座桥梁。当我们去尝试这种方式时,发现Controller中的代码变得非常少,变得易于测试和维护,只需要Controller和ViewModel做数据绑定即可,这也就我们常说的数据驱动视图。
1. Vue2实现对象数据的响应式
我们知道对象数据实现响应式是基于Object.defineProperty()的get()和set()方法,代码如下:
function defineReactive(obj, key, val) {
get(){
console.log('get '+ val)
return val
},
set(newVal){
console.log('set '+ key + ':'+ newVal)
val = newVal
}
}
由此我们可以实现Object对象上属性的响应式,当我们设置初始值是,会调用get()方法,获取到当前的val值,但我们主动去修改值时,会触发set方法,完成响应式实现值的变更,对象上存在多个属性,那么如果我们每用一个值调用一次会比较麻烦,这个时候我们会通过遍历,来将Object上每个属性都附有响应式效果,代码如下:
//对象响应化,遍历每个key,定义getter,setter
function observe(obj) {
if(typeof obj !== 'object' || obj == null){
return
}
if(Array.isArray(obj)){
//覆盖原型,替换7个变更操作
obj.__protp__ = arrayProto
//对数组内部的元素执行响应化
const keys = Object.keys(obj)
for(let i = 0;i <obj.length;i++){
observe(obj[i])
}
}else{
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
}
如果Object中属性依然是Object该怎么办?那么这个时候我们就需要用到了递归这个方法,解决代码如下:
function defineReactive(obj, key, val){
oberve(val)
///...
}
这里我们已经解决了对象数据的响应化处理,这个时候我们还遗漏了另外一种数据类型,就是数组(Array)了,这里我们提出了另外一种解决方案,将Array的原型prototype上的7个方法(push,pop,shift,unshift,splice,reverse,sort)增加通知更新的操作,代码如下:
//首先我们取出Array上的prototype
const originalProto = Array.prototype
//由于prototype具有链式关系,因此我们避免直接改动所有Array上的原型链方法,我们进行克隆备份
const arrayProto = Object.create(originalProto)
['push','pop','shift','unshift','splice','reverse','sort'].forEach(method => {
arrayProto[method] = function() {
//原始操作
originalProto[method].apply(this, arguments)
//通知更新
set(){
//...
}
}
})
数组原有方法的基础上,增加了数据监听依赖,以达到数组变化时候的 响应式更新,这样我们基本上成功实现了vue的响应式处理