存取描述符getter/setter只能偵測數據的修改和讀取,是沒法偵測數據的增長與刪除的。Object的增刪也是依靠delete()才能完成增刪與響應(能夠看個人Vue全局API裏有)編程
瞭解過Vue的會知道數組的變化必須使用Array的原生函數push
、pop
、unshift
、shift
、split
、splice
等數組
這些原生函數在數組的原型鏈上,在Array.prototype中瀏覽器
this.list.push(1) //向數組尾部添加元素
this.list.pop() //刪除數組尾部元素
...
複製代碼
在ES6以前沒有源編程能力,沒法攔截原型方法的能力。(ES6的Proxy有元編程能力)bash
因此爲了攔截原型方法,實現攔截器覆蓋Array.prototype。app
const arrProto=Array.prototype
cosnt arrayMethods=Object.create(arrProto)
這樣arrayMethods的__proto__就是Array.prototype,而arrayMethods屬性是空的{}一個對象
複製代碼
Obsrver{
constructor(value){
if(Array.isArray(value)){
value.__proto__ = arrayMethods
}
}
}
data(){
return{
list:[]
}
}
當變量是數組時修改它的原型鏈
本來list的原型鏈是list.__proto__ = Array.prototype = Object.prototype = null
修改後list的原型鏈是list.__proto__= {} = Array.prototype = Object.prototype = null
咱們就能夠在這裏實現攔截
複製代碼
const arrProto=Array.prototype
cosnt arrayMethods=Object.create(arrProto)
['push','pop','shift','unshift','splice','sort','reverse']
.forEach(function(method){
//獲取到原生方法
const origin = arrProto[method]
Object.defineProperty(arrayMethods,method,{
value :function(...args){
return origin.apply(this,args)
},
enumerable:false,//不可枚舉
writable:true,
configurable:true
})
})
好了這樣就實現了攔截
複製代碼
有的瀏覽器不支持訪問原型__proto__,Vue簡單粗暴直接設置到被偵測的數組自己身上。函數
//是否支持訪問原型
hasProto = '__proto__' in {}
複製代碼
function defineReactive (data, key, val){
if(typeof val === "object") new Observer
let dep = new Dep()
Object.defineProperty(data,key,{
enumerable:true,
configurable:true,
get : function(){
dep.depend()//這裏收集依賴
return val
},
set : function(newVal){
if(val === newVal){
return
}
dep.notify()
val = newVal
}
})
}
複製代碼
每當你讀取這個數組時都會觸發get的依賴收集ui
Array在get中收集依賴,在攔截器中觸發依賴this
這個依賴要保存在get和攔截器都能訪問的地方spa
那就是Observer(將變量從數據描述符變爲存取描述符的函數)中,而且這個Observer保存於值Value的不可枚舉_ob_中,你能夠打開Vue看一下。prototype
def(value,'_ob_',this)
fuction def(obj,key,val,enumerable){
Object.defineProperty(obj,key,{
value:val,
enumerable:!!enumerable,
configurable:true,
writable:true,
}
}
複製代碼
另外也會遍歷數組,偵測元素的變化
['push','pop','shift','unshift','splice','sort','reverse']
.forEach(function(method){
//獲取到原生方法
const origin = arrProto[method]
Object.defineProperty(arrayMethods,method,{
value :function(...args){
const ob = this._ob_
ob.dep.notify()
通知依賴,這裏數據發生了變化
return origin.apply(this,args)
},
enumerable:false,//不可枚舉
writable:true,
configurable:true
})
})
複製代碼
最後若是是push、unshift、splice這些能夠新增元素的話,暫存這些元素拿去修改成存取描述符
arg 就是 push(...arg)的形參
let inserted
switch(method){
case 'push' :
case 'unshift' :
inserted = arg
break
case 'splice':
inserted = arg.slice(2)
}
if(inserted) ob.observeArray(inserted)
這裏說一下splice爲何要arg.slice(2),splice要有至少三個參數纔是添加,否則是刪除
複製代碼
好了Vue2版本的就寫到這裏了,diff那塊雖然讀完了但懶得寫了,本來19年11月就讀完了源碼到如今才寫完。以後會去讀React的與源碼部分,Vue3.0等正式發佈後還要去讀源碼。
到時候在寫一些解析吧。