Vue 如何實現響應式原理
Vue 將MVVM做爲數據綁定的入口。
幾個Vue源碼關鍵核心概念:
- Observer 數據的觀察者,監聽自己的model數據變化
- Complie 解析編譯模板指令
- Watcher 數據訂閱者,是Observer 與 Complie 之間通信的橋樑(用來訂閱變化時執行相應操作的)。
進一步分析
雙向綁定流程:
Observer --data update–> Dep --notify通知–> Watcher --update–> View
-------------------------------Dep----<–subscribe訂閱—Watcher--------------------
Dep 是Observer 和 Watcher 之間的橋樑, Observer 觀察到數據變化則告知 Dep, Dep會向收集到的Watcher 訂閱者們(Dep 內會有一個數組用來收集依賴的訂閱者),發送數據改變的通知,收到通知後進行View更新。*
一個簡單的實現Demo實例:
class Vue{
constructor (options) {
this.$options = options
this._data = options.data
observer(options.data, this._update.bind(this))
this._update()
}
_update() {
this.$options.render()
}
}
function observer(obj, cb) {
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key], cb)
})
}
function defineReactive(obj, key, val, cb) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: ()=> {
console.log(`訪問了: ${key}`)
return val
},
set: newVal => {
if(newValue === val)
return
console.log(`設置了: ${key}`)
val = newVal
cb()
}
})
}
var vm = new Vue({
el: '#app',
data: {
text: "vm data attr"
},
render(){
console.log('runder function run.....')
}
})
vue 如何通過Dep 避免渲染無關值更新導致的渲染問題:
vue 會在Render 函數中,收集本次渲染相關的值,並處理相關值爲響應式。 所以,與渲染無關的值,並不會觸發get, 也就不會在依賴收集器中添加到監聽。 所以渲染無關的值,即使調用set賦值, notify中的subs也是空的。 因此不會觸發渲染。
注: 這裏“依賴收集器” 並非官網定義的名詞。 只是個人理解起名。
總結:
- vue將data 初始化爲一個Observer 並將對象中的每個值,重寫其get, set, data中的每個key, 都有一個獨立的依賴收集器;
- 在get中,向依賴收集器添加了監聽;
- 在mount 時, 實例了一個Watcher, 將收集器的目標指向了當前Watcher;
- 在data值發生變更時,觸發set, 觸發了依賴收集器中的所有監聽的更新, 從而觸發Watcher.update