Vue源代碼筆記(一)數據綁定

VUE數據綁定介紹

數據綁定是vue的基礎核心之一,本文以Vue對象(固然也包含VueComponent)裏的data數據綁定爲例,闡述整個綁定的過程。html

Vue的數據綁定由三部分組成,vue

  1. Observer 監聽Object裏屬性的變化,一旦有屬性發生變化,會發布帶有新值的通知。它是一個發佈者的角色。
  2. Watcher  接收新值的通知,對新值進行處理,好比刷新控件值(若是控件值綁定data裏的屬性)。它是一個訂閱者的角色。
  3. Dep Observer和Water的粘合劑。 Observer的通知是發給Dep,而後Dep再轉發給Watcher。 而Watcher做爲訂閱者,不是直接註冊在Observer裏,而是Dep裏。Dep可接收多個Watcher,必定程度來講也是個Watcher管理器。

如下是三者之間的關係圖 react

                                          

如下是來自於Vue的自己自帶的例子,由一個index.html和app.js組成。經過這個例子咱們來看一下,整個數據綁定的過程。數組

index.htmlapp

1 <!DOCTYPE html>
 2 <html>
 3   <head>
 4     <meta charset="utf-8">
 5     <meta name="viewport" content="width=device-width,initial-scale=1.0">
 6     <title>test</title>
 7   </head>
 8   <body>
 9     <div id="app">
10  {{message}} 11 <button-counter></button-counter> 12 </div> 13 <!-- built files will be auto injected --> 14 15 <script src="../../dist/vue.js"></script> 16 <script src="app.js"></script> 17 </body> 18 </html>

app.js函數

1 Vue.component('button-counter', { 2 data: function () { 3 return { 4 count: 0, 5  } 6  }, 7  methods:{ 8 onClick:function(){ 9 this.count++; 10  }, 11  }, 12 template: '<div><button v-on:click="onClick">{{count}}</button></div>' 13  }) 14 15 var app = new Vue({ 16 el: '#app', 17  data: { 18 message: 'Hello Vue!' 19  } 20 })

運行代碼後見下圖,咱們能夠看見例子(index.html)中的<div  id=app>{{message}} ...</div>  message屬性被監聽和綁定訂閱者。 Vue利用Object.defineProperty來監聽對象屬性值的變化和對變化的通知。ui

對用於Object.defineProperty裏定義的兩個方法get和set,分別對應了Watcher對象訂閱屬性變化通知給Watcher對象this

Watcher對象的訂閱過程

 其中,get: function reactiveGetter方法,用於Watcher訂閱此屬性變化通知。get方法會在該屬性值觸發讀(read)動做的時候觸發。以上圖中的message爲例,當發生相似代碼 「let val = message"或者"message"時,就會觸發get函數。 lua

從上面的get: function reactiveGetter方法代碼中,咱們能夠看到表示當Dep.target不爲空時,就進行Watcher的訂閱。Dep.target實際上指向的是一個Watcher對象。spa

當調用相似下列代碼時

Dep.target = watcher; // Watcher對象

this.messgae; 

就會觸發如下代碼

1  get: function reactiveGetter () { 2 const value = getter ? getter.call(obj) : val 3 if (Dep.target) { //Dep.taget指向一個Watcher對象 4 dep.depend() //當Dep.target不爲空時,調用Dep對象(dep)的depend()方法來進行watcher的訂閱 5 if (childOb) { 6  childOb.dep.depend() 7 if (Array.isArray(value)) { 8  dependArray(value) 9  } 10  } 11  } 12 return value 13 },

//代碼出自Vue源代碼目錄下的/src/core/instance/index.js

咱們再來看下depend作了什麼

1 export default class Dep { 2 static target: ?Watcher; 3  id: number; 4 subs: Array<Watcher>; 5 6  constructor () { 7 this.id = uid++ 8 this.subs = [] 9  } 10 11  addSub (sub: Watcher) { //將Watcher對象放入subs數組中,完成訂閱動做,有屬性變化後,Dep通知subs數組裏全部的watcher對象。 12 this.subs.push(sub) 13  } 14 15  removeSub (sub: Watcher) { 16 remove(this.subs, sub) 17  } 18 19  depend () { //Watcher注入Dep對象 20 if (Dep.target) { 21 Dep.target.addDep(this) 22  } 23  } 24 25  notify () { 26 // stabilize the subscriber list first 27 const subs = this.subs.slice() 28 for (let i = 0, l = subs.length; i < l; i++) { 29  subs[i].update() 30  } 31  } 32 } 33 34 // the current target watcher being evaluated. 35 // this is globally unique because there could be only one 36 // watcher being evaluated at any time. 37 Dep.target = null 38 const targetStack = []

//代碼出自Vue源代碼目錄下的/src/core/observer/dep.js

 

 從上面的Dep代碼中能夠看出,調用了Watcher對象(Dep.target)的addDep方法注入Dep對象,來訂閱屬性變化通知。 接下來,咱們再看一下addDep作了些什麼。

 

1 export default class Watcher { 2 3  ............ 4 5 /** 6 * Add a dependency to this directive. 7 */ 8  addDep (dep: Dep) { 9 const id = dep.id 10 if (!this.newDepIds.has(id)) { 11 this.newDepIds.add(id) 12 this.newDeps.push(dep) 13 if (!this.depIds.has(id)) { 14 dep.addSub(this) 15  } 16  } 17  } 18 19  ............. 20 21 } 22 23
//代碼出自Vue源代碼目錄下的/src/core/observer/watcher.js

 

 咱們能夠從addDep方法裏看到,最終調用了Dep的addSub方法。結合上面已列出的Dep代碼。最終將watcher對象放入了Dep對象的subs數組中,完成了訂閱動做。

Watcher對象接收屬性值變化通知

set: reactiveSetter方法,在屬性值執行寫操做時(就是被賦值),會被觸發。意味着相似代碼"this.message='hello again' "就會觸發set函數,執行下列代碼

1 set: function reactiveSetter (newVal) { 2 const value = getter ? getter.call(obj) : val 3 /* eslint-disable no-self-compare */ 4 if (newVal === value || (newVal !== newVal && value !== value)) { 5 return 6  } 7 /* eslint-enable no-self-compare */ 8 if (process.env.NODE_ENV !== 'production' && customSetter) 9  { 10  customSetter() 11  } 12 if (setter) { 13  setter.call(obj, newVal) 14 } else { 15 val = newVal 16  } 17 childOb = !shallow && observe(newVal) 18 dep.notify()//通知watcher對象,value值發生變化。 19 } 

//代碼出自Vue源代碼目錄下的/src/core/instance/index.js

在set函數中,調用dep.notify將最新的屬性值通知dep對象裏的全部watcher(保存在subs數組中),watcher對象調用update方法更新視圖(參照上面提供的Dep代碼)。

總結

1.Vue數據綁定,由Observer,Dep和Watcher組成。 Observer監測屬性變化,發送變化通知。 Watcher訂閱變化通知,根據通知裏的最新屬性值,更新視圖。 Dep連接Observer和Watcher,轉發Observer的通知到Watcher,Watcher經過Dep訂閱Observer的通知。

2.Observer利用Object.defineProperty定義的get方法,監控屬性的讀操做。在讀操做中調用get方法,若是Dep.target指向了一個Watcher對象,就調用Dep.depend-->Watcher.addDep-->Dep.addSub訂閱屬性變化通知。

3.Observer利用Object.defineProperty定義的set方法,監控屬性的寫操做(即更新屬性值),在寫操做中調用set方法,set方法裏調用Dep.notify-->Watcher.update來用更新的值更新視圖。

原文出處:https://www.cnblogs.com/andreitang/p/10158494.html

相關文章
相關標籤/搜索