存取描述符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等正式发布后还要去读源码。
到时候在写一些解析吧。