寫在前面:總算在今天仔仔細細的看了一遍雙向數據綁定的帖子,其實之前也看了很多,都是自己理解能力不太夠,再一個靜不下心(哭)。看完之后進行一下總結。如有理解不到位的歡迎指正,謝謝。
Vue的響應式:其實就是通過數據的改變去驅動DOM視圖的變化。這里是Vue最核心的內容。初始化時通過Object.defineProperty進行綁定,設置通知的機制。當編譯生成的渲染函數被實際渲染的時候,就會觸發getter進行依賴收集,在數據變化的時候,觸發setter進行更新。
首先一個小小入門例子。假設<p>標簽的內容會隨着obj中數據的改變而變化.。
1 <div id="app"> 2 <p id="name"></p> 3 </div> 4 <script> 5 var obj={} 6 obj.name="軟軟"//操作Object.defineProperty來使得name數據的改變影響p標簽 7 </script>
通過Object.defineProperty(obj, prop, descriptor)來實現。 obj:要在其上定義屬性的對象。prop:要定義或修改的屬性的名稱。descriptor:將被定義或修改的屬性描述符。
1 <div id="app"> 2 <p id="name"></p> 3 </div> 4 <script> 5 var obj={}; 6 Object.defineProperty(obj,'name',{ 7 get(){ 8 return document.querySelector('#name').innerHTML; 9 }, 10 set(newVal){ 11 document.querySelector('#name').innerHTML=newVal 12 } 13 }) 14 obj.name="軟軟"//操作Object.defineProperty來使得name數據的改變影響p標簽 15 </script>
接下來創建一個新的newVue.js。通過使用new NewVue(.....)來實現vue的響應式
1 class NewVue{ 2 constructor(options){//接收所想要配置的對象就像是new Vue({data:{...}}) 3 this.$options=options;//先緩存一下options一會其他的類要用到 4 //數據響應化 5 this.$data=options.data;//拿出{data:{}}的數據 6 this.observe(this.$data);//對data中的數據進行觀察. 7 } 8 observe(value){ 9 //為了語句健壯性,先判斷是否存在這個value,不存在就返回了 10 if(!value||typeof value !='object'){ 11 return; 12 } 13 //遍歷該對象 14 Object.keys(value).forEach(key=>{ 15 16 this.defineReactive(value,key,value[key]) 17 }) 18 } 19 //定義數據響應式函數 20 defineReactive(obj,key,val){ 21 this.observe(val)//為了遞歸實現類似{data:{foo.bar:"xxx"}}中的foo.bar這種數據嵌套的問題 22 Object.defineProperty(obj,key,{ 23 get(){ 24 return val; 25 }, 26 set(newVal){ 27 if(newVal==val){ 28 return; 29 } 30 val=newVal; 31 console.log(`${key}屬性更新了:${val}`) 32 } 33 }) 34 } 35 }
創建一個index.html實現這個響應式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="newVue.js"></script>
<script>
const app=new NewVue({
data:{
test:"fjs是粑粑",
foo:{
bar:"bar"
}
}
});
app.$data.test="hello fjs";
app.$data.foo.bar="oh bar"
</script>
</body>
</html>
最終實現的結果:

依賴收集
1 class NewVue{ 2 constructor(options){ 3 this.$options=options; 4 this.$data=options.data; 5 this.observe(this.$data); 6 //模擬一下watcher創建 7 new Watcher(); 8 this.$data.test; 9 new Watcher(); 10 this.$data.foo.bar; 11 } 12 observe(value){ 13 if(!value||typeof value !='object'){ 14 return; 15 } 16 Object.keys(value).forEach(key=>{ 17 this.defineReactive(value,key,value[key]) 18 }) 19 } 20 21 defineReactive(obj,key,val){ 22 this.observe(val) 23 const dep=new Dep() 24 Object.defineProperty(obj,key,{ 25 get(){ 26 Dep.target&&dep.addDep(Dep.target) 27 return val; 28 }, 29 set(newVal){ 30 if(newVal==val){ 31 return; 32 } 33 val=newVal; 34 dep.notify();//通知所有的watcher進行更新 35 } 36 }) 37 } 38 } 39 40 class Dep{//訂閱器 41 constructor(){ 42 this.deps=[];//在deps中存放若干依賴,也就是watcher(訂閱者).這個依賴其實就是屬性值發生改變的屬性 43 44 } 45 addDep(dep){//增加依賴 46 this.deps.push(dep) 47 } 48 notify(){//通知所有的依賴(watcher)去做更新 49 this.deps.forEach(dep=>dep.update()) 50 } 51 } 52 53 class Watcher{//訂閱者其實就是用來做具體更新的那個對象 54 constructor(){ 55 //將當前這個Watcher實例指定到Dep的靜態屬性target 56 Dep.target=this;//這個target只有一個,當有第二個watcher出現時就會覆蓋成第二個watcher 57 } 58 update(){ 59 console.log('屬性更新了!!!') 60 } 61 }
