面試官,你再試試問Vue響應式原理?(標題黨)

前排勸退

這文章不會再詳細介紹Vue響應式原理、源碼,由於相似的優秀文章掘金一大推。node

要了解Vue響應式源碼的話,建議去看看別人的詳細文章,此文章默認你已經掌握了響應式原理,畢竟我記錄下來也是爲了應付面試官。(狗頭)web

由於每次面試官問Vue響應式原理,本身語言都組織很差,因此乾脆提早在這演戲一下,把我認爲要回答的東西寫下來。面試


正文

要講清楚響應式原理,必需要先搞清楚Vue源碼中Observer Watcher Dep3個類具體的做用、以及他們之間的關係。數組

若是咱們把這3個類的關係、鏈接講清楚了,那麼響應式的大概原理也就明白了。app

Observer,其主要做用是對數據進行深度監聽、劫持,使得每次取值、設值都能監聽到,從而執行一些自定義的方法。dom

WathcerwatchObserver 處理過的數據,負責渲染視圖和更新視圖的,在響應式裏的它是一個渲染Watcher編輯器

Dep,是記錄渲染Watcher,和通知渲染Wathcer去更新視圖的。若是不記錄,數據更改後,Dep不知道要通知誰。函數


數據觀察者 Observer

咱們從響應式的入口,Observer開始講起。post

若是你傳入的data是一個對象,那麼Observer會對你的這個對象的每個key值進行defineReactive,其實也就是對data進行深度遞歸,把data轉化爲gettersetter的形式,底層也就是Object.defineProperty性能

那麼在getter和setter會作一些什麼呢?

Getter

Observer會給每個屬性,new一個Dep實例來進行管理屬性依賴狀況,當你對這個data(Object)取值時(也就是觸發get時),Dep實例會(調用dep.depend方法)把當前渲染Watcher記住

Setter

當你對這個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方法。


依賴數據調度中心 Dep

在說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可能被多個屬性所依賴


訂閱者 Watcher

dep裏面的subs數組裏,存放的watcher是渲染Watcher,其做用就是渲染、更新視圖。

當調用dep.notify的時候,會對這個屬性全部的watcher調用update方法。

update方法的做用,就是去更新視圖,這裏爲了節約性能會使用nextTick優化,相似一個防抖。

整個大概就是流程就是:

調用update以後,用nextTick優化。再調用更新的回調函數。

這個更新回調函數,生成新的render函數,render就會產生新的Vnode,vnode生成真實dom渲染視圖。

至此就完成了數據更改到模板更新的過程。

本文使用 mdnice 排版

相關文章
相關標籤/搜索