最近接觸了一些面試者,當我問起「Vue 如何實現數據雙向綁定」時,會脫口而出「數據劫持」,而後呢?而後就沒有而後了╮(╯_╰)╭。確實,「數據劫持」是基礎,但遠不是面試官想聽到的答案,不如花個十分鐘看看本文,下次照着回答就行了javascript
要解答問題,首先要理解問題: 數據雙向綁定 是一種模式,web語境下通常指數據從dom到JS對象之間的自動同步。DOM 與 JS 被隔離在兩個不一樣的運行時上,互相之間須要經過命令式的 DOM接口 溝通:DOM 須要正確觸發事件,將信息傳輸給JS程序;而JS也須要在狀態變動後,有意識地調用適當的接口,改變DOM內容。這種方式會引發兩個問題:vue
雙向綁定經過各類各樣的設計,將數據從 DOM 到 JS 或者從 JS 到 DOM 的同步過程,封裝在框架自己,上層代碼脫離了對底層接口的依賴,只須要關注狀態管理邏輯。java
咱們要討論的第一個問題是,如何檢測 JS 對象屬性發生的變動?最簡單粗暴的方法是「髒檢查」,舊版本的Angular就是用的這種方法,在各類可能引起狀態變動的事件後,啓動一次髒檢查。這種方法很直觀,但實現上須要考慮不少問題:git
setTimeout
、requestNextAnimationFrame
等)Vue 則採用元編程接口 Object.defineProperty
實現的。 在組件初始化,會調用該接口,將對象屬性包裝爲get
、set
函數,將代碼「埋入」屬性是「獲取」、「修改」行爲中。看個簡單例子,直觀感覺下:github
const person = {};
// 嘿嘿,誰都改不了個人名字
Object.defineProperty(person, 'name', {
get() {
return 'van';
},
set(v) {
console.log('they want change my name');
}
});
console.log(person.name);
// van
person.name = 'tec';
// they want change my name
console.log(person.name);
// van
複製代碼
Object.defineProperty
只是解決了狀態變動後,如何觸發通知的問題,那要通知誰呢?誰會關心那些屬性發生了變化呢?在 Vue 中,使用 Dep 解耦了依賴者與被依賴者之間關係的肯定過程。簡單來講:web
第一步,經過 Observer 提供的接口,遍歷狀態對象,給對象的每一個屬性、子屬性都綁定了一個專用的 Dep
對象。這裏的狀態對象主要指組件當中的data
屬性。面試
第二步,建立三中類型的watcher:編程
computed
屬性轉化爲 watcher
實例watch
配置轉化爲 watcher
實例render
函數綁定 watcher
實例第三步,狀態變動後,觸發 dep.notify()
函數,該函數再進一步觸發 Watcher 對象 update
函數,執行watcher的從新計算。設計模式
對應下圖:數組
注意,Vue 組件中的 render
函數,咱們能夠單純將其視爲一種特殊的 computed
函數,在它所對應的 Watcher
對象發生變化時,觸發執行render,生成新的 virutal-dom 結構,再交由 Vue 作diff,更新視圖。
本文到這裏就結束了,更多內容能夠嘗試看看源碼,代碼裏面的設計模式很是值得學習。
Vue 使用數據劫持做爲底層支撐,又設計了一套精妙的依賴管理方案解耦依賴。但數據劫持方案也有其難以解決的痛點: