Vue中的响应式数据经过Object.defineProperty实现,但这个方法不能劫持数组。
首先咱们定义一个要响应式的数据vue
let obj ={ name:'qc', location:{adress:'hz'}, arr:[1] }
在建立一个渲染notify函数,用来提示咱们的数据是否被渲染提示。数组
function notify(){ console.log("视图更新了!") }
接下来开始正式写代码,设想当咱们修改了obj对象里的数据,命令窗口就会提示视图更新了,而且数据已被修改。
建立一个observer函数,目的是遍历此对象的属性。函数
function observer(obj){ if(typeof obj == 'object'){ for (const key in obj) { defineReactive(obj,key,obj[key]) } } }
建立一个defineReactive函数,目的用来劫持数据。this
function defineReactive(data,key,value){ // console.log(data,key,value); Object.defineProperty(data,key,{ get(){ if(typeof value == 'object'){ observer(value) } return value }, set(newValue){ notify() value=newValue } }) }
Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。
Object.defineProperty具体查看mdn文档
经过observer函数遍历出obj属性,传给defineReactive,在此函数内用Object.defineProperty,get方式拿到当前的值value,set方式能够拿到设置的新的值,新值赋给老值就完成了,数据更新。
让咱们看下效果spa
observer(obj) obj.name='aa' console.log(obj.name)
目前完成的只能遍历对象一层,若出现多层的话,咱们能够采起递归劫持。prototype
function defineReactive(data,key,value){ // console.log(data,key,value); Object.defineProperty(data,key,{ get(){ if(typeof value == 'object'){ observer(value) } return value }, set(newValue){ notify() value=newValue } }) }
obj.name='aa' obj.location.adress='bj' console.log(obj.location.adress);
以上方式都不能对数组响应式,vue对数组采用了另外一套方式,由于数组自己的方法就能够让数组更新例如push,splice。
首先咱们先遍历出那些能够让数组更新的方法code
let mothod =['pop','shift','unshift','sort','reverse','splice','push']
这里为了让你们看到notify函数执行视图更新了,咱们copy一份数组的原型,在copy的上面去修改。server
let arrayProto=Array.prototype//先获取到原来的原型上的方法 ,为了修改数组里原生方法 添加一个render函数操做 let proto =Object.create(arrayProto)//复制一个新原型对象 跟旧的无关 mothod.forEach(mothod=>{ proto[mothod]=function(){ notify() arrayProto[mothod].call(this,...arguments) } })
在修改observer函数xml
function observer(obj){ if(Array.isArray(obj)){ obj.__proto__=proto return } if(typeof obj == 'object'){ for (const key in obj) { defineReactive(obj,key,obj[key]) } } }
修改defineReactive函数对象
function defineReactive(data,key,value){ // console.log(data,key,value); Object.defineProperty(data,key,{ get(){ if(typeof value == 'object'){ observer(value) } if(Array.isArray(value)){ observer(value) } return value }, set(newValue){ notify() value=newValue } }) }
让咱们去火烧眉毛的去看下效果
observer(obj) obj.arr.push(22) console.log(obj.arr); //不支持 数组内容直接改变例如arr[1]=11,不支持数组length-- 都不会发生数据响应
这样咱们就实现了简易的数据响应式
注意由于vue数据响应都是绑在data属性里面,因此你给一个对象添加一个新的属性时,是不会生效数据响应的,不过vue中提供了$set方法,能够动态的添加响应式数据,咱们再次也能够去实现下。
function $set(data,key,value){ if(Array.isArray(data)){ return data.splice(key,1,value) //当前key 改一个 值是value 触发 数组更新 } defineReactive(data,key,value) }
动态添加属性
$set(obj,'a',1) obj.a=2 console.log(obj.a);
动态添加数组
obj.arr.push(22) $set(obj.arr,0,2) console.log(obj.arr)
vue响应式数据就是依靠Object.defineProperty来实现的,可是缺点是数组没法实现,因此vue当遇到数组时会启动另外一套方法,这方法就是利用数组自己的方法例如 push,splice 来触发 数组更新。