Vue響應式數據
Vue中的響應式數據通過Object.defineProperty實現,但這個方法不能劫持數組。
首先我們定義一個要響應式的數據
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函數,目的用來劫持數據。
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方式可以拿到設置的新的值,新值賦給老值就完成了,數據更新。
讓我們看下效果
observer(obj)
obj.name='aa'
console.log(obj.name)
目前完成的只能遍歷對象一層,若出現多層的話,我們可以採取遞歸劫持。
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。
首先我們先遍歷出那些可以讓數組更新的方法
let mothod =['pop','shift','unshift','sort','reverse','splice','push']
這裏爲了讓大家看到notify函數執行視圖更新了,我們copy一份數組的原型,在copy的上面去修改。
let arrayProto=Array.prototype//先獲取到原來的原型上的方法 ,爲了修改數組裏原生方法 添加一個render函數操作
let proto =Object.create(arrayProto)//複製一個新原型對象 跟舊的無關
mothod.forEach(mothod=>{
proto[mothod]=function(){
notify()
arrayProto[mothod].call(this,...arguments)
}
})
在修改observer函數
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 來觸發 數組更新。