響應基於 set 和 get(Object.defineProperty)
類型:
單向綁定
雙向綁定
簡單例子(基於Object.defineProperty)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>
<form action="" class="form">
<div>
<label for="name">name</label>
<input type="text" name="name" id="name">
</div>
<div>
<label for="age">age</label>
<input type="text" name="age" id="age">
</div>
</form>
</div>
</body>
<script>
// 實現Model到View的單向綁定
//Model
let person = {
data: {
name: '',
age: '',
}
};
//ViewModel
function binding(obj) {
for (let key in obj.data) {
Object.defineProperty(obj, key, {
get() {
console.log("getter");
return obj.data.key;
},
set(value) {
console.log("setter: " + value);
let dom = document.querySelector(`#${key}`);
dom.value = value;
console.log("dom.value: " + dom.value);
obj.data.key = value;
console.log("obj.data.key: " + obj.data.key);
},
});
}
}
binding(person);
// 實現view到model的單向綁定
//View
//獲取表單對象
// let form = document.querySelector(".form");
// //監聽form的input事件
// form.addEventListener('input', (e) => {
// let value = e.target.value;
// let name = e.target.getAttribute('name');
// person[name] = value;
// });
</script>
</html>
分析:
1.js部分划分為數據(model)、關聯視圖方法viewModel
2.只需要使用binding方法,此方法就會遍歷觀測對象的所有key,並為key定義屬性攔截器(Object.defineProperty),在數據set、get時定義額外的處理
3.特別地,在set的時候,不僅僅更新的觀測對象的的值,還更新了DOM元素的值,當然這里是做了簡單處理,用以了解響應設計基礎部分,詳細部分的話,有興趣可以去看看官方源碼設計
4.大家可能注意到有一個部分被我注釋了,那個就是那個addEventListener這部分,這里實現了view到model的單向更新
總結: vue的響應式核心是基於ES5特性Object.defineProperty生成的set、get
Vue對數組和對象的觀測處理:
非根級變量響應方法:
1.數組(vue對這些數組方法進行了代理,調用時做了更新視圖的處理),至於為什么,下面馬上解釋
檢測邊界:
1.增加或者修改
新增:vm.items[indexOfItem]=newValue
這種情況其實就是在為對象添加一個新key,數組本身也是Array類的一個實例
修改:vm.items[indexOfItem]=newValue按照數組本身是一個對象應該會觸發更新,但是vue設計源碼沒有把數組對象的key進行set和get轉換
2.修改長度
vm.items.length = newLength
同樣這樣沒有set和get沒法觸發視圖更新
另辟蹊徑:
通過對原生方法進行改寫來觸發視圖更新
1.變異方法(改變原數組)
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
2.非變異方法(返回新數組),vue在更新視圖對這種覆蓋原數組的情況作了優化處理,無需擔心渲染效率
filter()
contact()
slice()
檢測邊界的新處理方法:
1.增加元素
1.1 按順序插入
push(value)
unshift(value)
1.2 按指定key插入
Vue.$set(arr,index,value)
2.刪除元素
splice(index,count)
3.修改元素
splice(index,1,value)
2.對象(遍歷data賦予set和get)
Vue.$set(target, key, value)
注意事項:被設置變量不能存在(源碼發現key存在就會跳過此key的set和get轉換)
增加單個屬性
Vue.$set(target, key , value)
增加多個屬性
Object.assign({},target,source)
