由於剛開始學習 vue 原始碼,而且水平有限,有理解或表述的不對的地方,還請不吝指教。
vue 主要通過 watcher、dep 和 observer 三個類來實現響應式檢視。另外還有乙個 scheduler 來進行排程,本次暫時不做討論。
watcher 和 dep 是訂閱者和發布者的關係,每個 watcher 可以訂閱多個 dep,而每個 dep 也可以被多個 watcher 訂閱。當 observer 監聽的資料發生改變時,相應的 dep 就會觸發其訂閱者 watcher 更新檢視。
下面通過乙個簡單的例子來說明其實現流程和原理:
頁面初始會顯示 "init" 字串,3秒鐘之後,會更新為 "changed" 字串。
為了便於理解,將整個流程分為三個階段:
初始化data,這個階段 vue 通過 observer 監聽 data 物件,並將普通的 somevar 屬性**為 get\set 屬性
初次掛載el,這個階段 vue 使用預設的 somevar 資料渲染檢視,並將 watcher 新增到 dep 的訂閱者列表
somevar 更改觸發檢視更新,這個階段 somevar 被賦予了新值,vue 根據 watcher 和 dep 的訂閱關係觸發檢視的更新
下面我們來逐步分析這三個階段的流程。
new vue(options) => vm._init(options) => initstate(vm) => initdata(vm) => observe(data) => new observer(data) => definereactive(data, key, value)
初始化操作會監聽 data 物件,對其每乙個屬性呼叫 definereactive() 方法,將其改造為響應式屬性,**如下(去掉了不影響表述主流程的**,以便能更清晰的抓住重點):
/**
* define a reactive property on an object.
*/export function definereactive (
obj: object,
key: string,
val: any
) , set: function reactivesetter (newval)
val = newval
dep.notify()}})
}
可以看到,當 get() 方法執行的時候,會呼叫 dep.depend() ;當 set() 方法執行時,會呼叫 dep.notify()。後面我們會看到這兩個方法的作用。
vm.$mount(el) => mountcomponent(vm, el) => new watcher(vm, updatecomponent) => watcher.get() => updatecomponent() => vm._update(vm._render())
vm._render() 呼叫 vm.$options.render() 方法生成 vnode,vm._update() 方法根據 vnode 對檢視做更新。
vm.$options.render() 方法是在 $mount() 方法中生成的,生成後的**如下:
(function()
}, [_v("\n " + _s(somevar) + "\n ")])
}})
可以看到,**中會使用到 vm.somevar 屬性,而該屬性最終會**到之前定義的響應式屬性上,從而呼叫其 get() 方法,進而呼叫 dep.depend() 方法將 watcher 新增到訂閱者列表。
dep.depend() => dep.target.adddep(dep) => dep.addsub(watcher) => dep.subs.push(watcher)
dep.subs 就是 dep 的訂閱者列表,通過這個流程,就建立起了 dep 和 watcher 之間的訂閱關係。
其中,dep.target 就是當前的 watcher,因為在 watcher.get() 方法執行時,有如下流程:
watcher.get() => pushtarget(watcher) => dep.target = watcher
3秒之後,vm.somevar 被賦予了新的值,從而最終會呼叫到響應式屬性的 set() 方法,進而呼叫 dep.notify(),觸發 watcher 更新檢視。
dep.notify() => watcher.update() => watcher.run() => watcher.get() => watcher.getter.call(vm, vm) == updatecomponent() => vm._update(vm._render())
watcher 的 getter() 方法就是第二階段 new watcher(vm, updatecomponent) 中的 updatecomponent 方法,可以看到,通過這個流程,檢視得到了更新。
以上就構成了乙個響應式檢視模型,其核心是利用 defineproperty() 方法將普通屬性轉換為帶有鉤子的 set\get 屬性,從而實現了資料監聽。
Vue 響應式原理 原始碼
整個函式結束,相當於初始化所有屬性和vue內建事件 如 emit 並且使所有屬性變為響應式。初始化所有option api 對其中的使用者自定義資料data 進行 observe 此函式用來新建乙個類observer的例項,類observer的constructor中用walk 函式進行遍歷每個屬性...
結合原始碼解析 vue響應式原理
vue響應式原理 在之前vue2.0中,vue的響應式原理主要是基於object.defineproperty進行資料劫持然後結合觀察者模式 發布訂閱模式 來實現資料的雙向繫結。function dep dep.prototype.notify function function sub x sub...
vue原始碼學習 響應式資料
vue原始碼學習 初始化data vue原始碼學習 響應式資料 在 vue原始碼學習 初始化data 一文中,知道了在new vue 時做了一系列初始化操作,其中在初始化data資料時,利用observe data,true 方法,對資料屬性進行了觀察。下面來具體看下是如何對data進行的觀察,從而...