哈嘍~~~vue
最近在複習一些常見的設計模式,聯想到vue的響應式原理(2.x),決定整理一篇淺顯易懂的文章闡述其核心思想。react
ok,讓咱們開始。設計模式
發佈訂閱模式基於一個主題事件通道,接收通知的對象能夠經過自定義的事件訂閱主題,對象會在主題事件發佈時收到通知。函數
說到發佈訂閱模式,不得不提觀察者模式,二者都是維護事件與依賴該事件的對象之間的關係,在有關狀態發生變動時會執行相應的更新,因此也常常有人將二者混爲一談,其實二者的差別也十分明顯,在發佈訂閱模式中事件統一由調度中心處理,且能夠基於不一樣的主題去執行不一樣的事件,實現了徹底的解耦,更加靈活多變。ui
let pubSub = {
subscribers: {},
subscribe(key, fn) {
if (!this.subscribers[key]) this.subscribers[key] = [];
this.subscribers[key].push(fn);
},
unSubscribe(key) {
delete this.subscribers[key];
},
publish(key, ...args) {
let listeners = this.subscribers[key];
listeners.forEach(fn => fn(...args)); // 通知全部訂閱者
}
};
/* 爲簡化代碼,省去了一些錯誤邊界的處理,調試: pubSub.subscribe('event', () => { console.log('first event'); }); pubSub.subscribe('event', () => { console.log('second event'); }); pubSub.publish('event'); // first event second event */
複製代碼
看到這你大概明白了,發佈訂閱模式就是經過在事件調度中心(subscribers)添加訂閱者的事件(subscribe),等到發佈事件(publish)時執行相應的事件就能夠了,接下來,咱們看看vue是如何一步步實現響應式系統。this
👆是vue官網下載的一張圖,用來描述vue是如何經過收集的依賴,在data變動的時候觸發通知變動的過程。如今看上去仍是有點抽象,下面咱們從源碼的角度梳理主要的幾個步驟對其進行一些拆分。spa
// vue/src/core/observer/index.js
const dep = new Dep() // 實例化依賴管理器 ---- 下面會介紹
function defineReactive (obj,key,val) { // 將對象轉化成可觀察對象
if (arguments.length === 2) {
val = obj[key]
}
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
dep.depend() // 每一個data的屬性都會有一個dep對象,用來進行收集依賴
return value
},
set: function reactiveSetter (newVal) {
if(val === newVal){
return;
}
val = newVal;
dep.notify() // 通知依賴更新
}
})
}
// vue/src/core/observer/dep.js
class Dep { // 訂閱者Dep 用來存放Watcher觀察者對象 也被稱爲依賴管理器
static target: ?Watcher;
subs: Array<Watcher>;
depend () {
if (Dep.target) {
Dep.target.addDep(this) // 添加一個Watcher對象
}
}
addSub (sub) {
this.subs.push(sub)
}
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update() // 通知全部Watcher對象更新視圖
}
}
}
複製代碼
// vue/src/core/observer/watcher.js (如下爲簡化的代碼片斷)
export default class Watcher {
constructor (vm,expOrFn,cb) {
this.vm = vm;
this.cb = cb;
this.getter = parsePath(expOrFn) // 這個函數會調用組件的render函數來更新虛擬DOM
this.value = this.get()
}
get () {
Dep.target = this // 將當前的Watcher賦值給了Dep.target
const vm = this.vm
let value = this.getter.call(vm, vm) // 調用組件的更新函數
Dep.target = undefined
return value
}
addDep (dep) {
dep.addSub(this) // 將Watcher對象註冊到該屬性的Dep中 --- 依賴收集
}
update () { // 接收到變動時執行更新
const oldValue = this.value
this.value = this.get()
this.cb.call(this.vm, this.value, oldValue)
}
}
複製代碼
拆分完成,若是還有不清楚的地方,那下面這張圖形象的解釋了Watcher、Dep和Observer三者之間的關係,結合以前對源碼的分析,能夠看到Observe扮演的角色是發佈者,對對象的每個屬性進行數據監聽,Watcher是鏈接組件和data的橋樑,Dep則扮演是一個收集依賴和管理通知更新的調度中心。設計
(Tips: 筆者認爲弄明白這幾個概念對於理解響應式原理很是重要,在此基礎上閱讀源碼效果也會好的多。)調試
本文介紹了發佈訂閱模式的原理和簡單實現,並根據流程梳理簡述了vue響應式原理的核心思想,文章不長,但願對你有所幫助。code
時間倉促,文中確定會有一些不夠嚴謹的地方,歡迎你們指正和討論。
最後,感謝閱讀!