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