這文章不會再詳細介紹Vue響應式原理、源碼,由於相似的優秀文章掘金一大推。node
要了解Vue響應式源碼的話,建議去看看別人的詳細文章,此文章默認你已經掌握了響應式原理,畢竟我記錄下來也是爲了應付面試官。(狗頭)web
由於每次面試官問Vue響應式原理,本身語言都組織很差,因此乾脆提早在這演戲一下,把我認爲要回答的東西寫下來。面試
要講清楚響應式原理,必需要先搞清楚Vue源碼中Observer
Watcher
Dep
3個類具體的做用、以及他們之間的關係。數組
若是咱們把這3個類的關係、鏈接講清楚了,那麼響應式的大概原理也就明白了。app
Observer
,其主要做用是對數據進行深度監聽、劫持,使得每次取值、設值都能監聽到,從而執行一些自定義的方法。dom
Wathcer
,watch
被 Observer
處理過的數據,負責渲染視圖和更新視圖的,在響應式裏的它是一個渲染Watcher
。編輯器
Dep,是記錄渲染Watcher
,和通知渲染Wathcer
去更新視圖的。若是不記錄,數據更改後,Dep不知道要通知誰。函數
咱們從響應式的入口,Observer
開始講起。post
若是你傳入的data是一個對象,那麼Observer
會對你的這個對象的每個key值進行defineReactive
,其實也就是對data
進行深度遞歸,把data
轉化爲getter
和setter
的形式,底層也就是Object.defineProperty
性能
那麼在getter和setter會作一些什麼呢?
Observer會給每個屬性,new一個Dep實例來進行管理屬性依賴狀況,當你對這個data(Object)取值時(也就是觸發get時),Dep實例會(調用dep.depend方法)把當前渲染Watcher記住。
當你對這個data(Object)進行設值時(也就是觸發了set方法),須要更新模板了,就讓dep通知以前記住的watcher去更新(調用dep.notify)
到這裏,咱們大概知道了Observer、Dep、Watcher的關係。
附上一張,偷過來的圖.(狗頭)
簡單來講,就是Observer進行數據劫持、Watcher更新視圖,而Dep是Observer和Watcher的橋樑,記錄了數據的更改由哪一個Watcher更新。
帶着上面的總結,繼續聊聊Observer。
上面咱們說到,若是data是一個Object,就defineReactive,使用Object.defineProperty
來劫持。
那麼若是data是一個數組呢?
實際上在響應式裏,對數組的處理,是調用了另一套機制---重寫Array原型方法。
總得來講就是,你在調用數組方法的時候,好比push,會先調用本身定義的push,再執行原生Array的push方法。
實現細節就是:
一、先拷貝數組的原型
let arrayMethods = Object.create(Array.prototype)
複製代碼
二、接着在本身建立的原型對象中,重寫7個會改變數組自己的方法
// 這七個方法均可以改變原數組
let methods = [ 'push', 'pop', 'shift', 'unshift', 'sort', 'reverse', 'splice' ] methods.forEach(method=>{ arrayMethods[method] = function (...args) { // 而後當你調用methods裏的方法的時候,進行對數據observe // 而後再調用原生數組的方法 Array.prototype[method].apply(this,args); } }) 複製代碼
三、最後更改數據的原型鏈
data.__proto__ = arrayMethods;
複製代碼
那麼,當你執行data.push
的時候,就會先執行arrayMethods
裏面的push,最後在arrayMethods裏面,處理完本身的邏輯,再調用Array.push便可達到改寫原生Array的效果。
由於這個機制,使得不用循環遍歷數組的每一項進行觀察,也是由於這個機制致使了直接調用數組的下標沒法更新視圖,須要調用set方法。
在說Observer
的時候,咱們說過在獲取數據的時候,會調用dep.depend
進行依賴收集,其實就是把對應的Watcher push到subs數組中。
有了這個數組後,若是你在對一個屬性設置,那麼就調用dep.notify,其實就是把subs裏的每個watcher去執行更新。
notify(){
this.subs.forEach(watcher=>watcher.update()) } 複製代碼
dep 和 watcher 是多對多的關係
每一個屬性 都有一個dep屬性 ,dep 存放着watcher .
---dep中能夠有多個watcher ,由於一個watcher可能被多個屬性所依賴
dep裏面的subs數組裏,存放的watcher是渲染Watcher,其做用就是渲染、更新視圖。
當調用dep.notify的時候,會對這個屬性全部的watcher調用update方法。
update方法的做用,就是去更新視圖,這裏爲了節約性能會使用nextTick優化,相似一個防抖。
整個大概就是流程就是:
調用update以後,用nextTick優化。再調用更新的回調函數。
這個更新回調函數,生成新的render函數,render就會產生新的Vnode,vnode生成真實dom渲染視圖。
至此就完成了數據更改到模板更新的過程。
本文使用 mdnice 排版