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 來觸發 數組更新。