除了理解该原理外,日后可用于需要用不同方式处理对象、数组及基本类型数据的场景
vue对于数据的响应式是直接调用observe(data),将option中的data传入
export function observe(data){
// 后续递归响应object类型数据(包括array)
if(typeof data !== 'object' || data === null){
return
}
return new Observe(data)
}
接下来看一下Observe这个类
Observe
在上一步中已经将数据进行过滤,所以这里进来分别处理对象和数组
walk、arrayObserve
class Observe{
constructor(data){
if(Array.isArray(data)){
this.arrayObserve(data)
}else{
this.walk(data)
}
}
walk(data){
for(let key in data){
defineReactive(data,key,data[key])
}
}
arrayObserve(value){
for(let i = 0;i < value.length;i++){
observe(value[i])
}
}
}
由于数组中可能存在对象或数组,所以递归使用observe(对象中存在的相关情况会在defineReactive中处理)
接下来看下对object的处理函数defineReactive
function defineReactive(data,key,value){
observe(value)
Object.defineProperty(data,key,{
get(){
...
console.log('收集依赖')
return value
},
set(newValue){
observe(value)
value = newValue
...
console.log('更新操作')
}
})
}
在处理object时,object的值也可能是object (例如father.wife = {name:'merry'}),因此需要递归调用observe处理
而在给某个赋值的时候,可能给的也是对象,因此需要在set中递归调用下observe
响应式的处理也主要是在get和set中进行
数组方法的处理
接下来还有一个问题,就是在调用数组方法push,shift,unshift...时,也需要进行响应式操作
这里需要改造一下Observe类,如下
...
constructor(data){
// 添加__ob__一个作用是标志为响应式对象,还有就是在更改数组操作时要用到
Object.defineProperty(data,'__ob__',{
configurable:false,
enumerable:false,
value:this
})
if(Array.isArray(data)){
data.__proto__ = arrayMethods
this.arrayObserve(data)
}else{
this.walk(data)
}
}
...
下面时arrayMethods的相关操作,主要是修改了相关数据的原型
先保留Array的原型到变量oldArrayMethods上,接着继承并暴露arrayMethods,
利用一个数组保存需要改造的methods,然后在后面的操作中对其进行响应式操作
let oldArrayMethods = Array.prototype
export let arrayMethods = Object.create(oldArrayMethods)
const methodsToPatch = [ 'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse']
利用原型链的屏蔽规则,给对象添加相关属性时,先调用原型链上的方法,以此保证程序的正常进行,并在最后返回结果
对于上述操作,当插入数据时需要对其进行响应式操作,插入的操作主要包括push,unshift和splice,这三者的插入元
素分别如下
push:第1个参数即为插入元素
unshift:第1个参数即为插入元素
splice:第3个参数即为插入元素
当检测到有插入元素时,调用observeArray进行更新,代码如下
methodsToPatch.forEach(method=>{
arrayMethods[method] = function(...args){
let result = oldArrayMethods[method]apply(this,args)
let ob = this.__ob__
let insert = null
switch(method){
case 'push':
case 'unshift':
insert = args
break;
case 'splice':
insert = args.slice(2)
break;
}
if(insert) ob.observeArray(insert)
console.log('更新操作')
return result
}
})