原文連接個人blog,歡迎STAR。javascript
首先安利一波福利,有沒有用vscode的小夥伴?推薦一個神奇的字體,自從用了這個字體,敲代碼效率簡直上天了。先上圖看看效果:vue
還有其餘許多,就不一一列舉出來了。
有沒有看上了的?
沒有我等下再來問。java
此次推薦的一篇文章來自這,閱讀這篇文章有利於加深對Vue程序結構的瞭解,雖然是 1.0版本,不過好在 2.0 版本保留了絕大部分 1.0 的API。node
在這篇文章裏我將是這幾個月來對 Vue 學習的一個小結。react
Vue 和其餘的 MVVM 思路是相似的:git
主要是爲了實現三個過程:github
Observer: 經過Observer對data進行監聽,而且提供訂閱某個數據項的變化的能力。利用Object.defineProperty
, 將data裏的每一個屬性所有轉化爲getter/setter,已遍攔截對象賦值與取值操做;算法
Compiler: 將template 解析爲 render()方法;數組
watcher: Compiler 的解析結果與 Observer 結合起來,在 Observer 監聽到數據發生改變時,接受通知,進而觸發 re-render, 從新渲染DOM。框架
咱們從 new Vue
開始,
上圖便是官網給出的一張生命週期圖,主要是四個過程:
如今,咱們進入源碼,分析具體的實現:
new vue()
的時候,會進入_init,能夠看出在 beforeCreate
與 created
只有initState
, initState
是用於實現data observer
和 event/watcher
。
_init
最後,會運行 vm.$mount
方法:具體 vm.$mount
的分析,請看上篇。最後進行了 render()
, 從而會有Vnode
, 通過 DIFF 算法後會有真實 DOM ;
Update: DOM 以後,會進行update方法:
vm._watcher = new Watcher(vm, () => {
vm._update(vm._render(), hydrating)
}, noop)複製代碼
將以上用一張序列圖表示也就是:
MVVM 框架有一個很重要的特徵:就是當數據放生變化後,會自動更新對應的DOM節點。 Vue 是怎麼實現的?
以上是來自官網的一張圖。
前面提到在 beforeCreate
與 created
兩個生命週期鉤子函數之間會運行 initState()
方法。
initState() 源碼裏:
在這個方法裏,會對props, data, computed
等屬性利用 Object.defineProperty
將這些屬性所有轉化爲 getter/setter
。
咱們以 initData()
爲例子進行分析:
這裏有一個值得注意的地方,
proxy(vm, "_data", keys[i])
, 設置vm._data
爲代理,具體做用是實現vm.a === vm._data.a
。
在 initData()
最後 會進行 observe(data, this)
。
在observe()裏,既是轉化爲 getter/setter
。
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
// 只有在有Dep.target時才說明是Vue內部依賴收集過程觸發的getter
// 那麼這個時候就須要執行dep.depend(),將watcher(Dep.target的實際值)添加到dep的subs數組中
// 對於其餘時候,好比dom事件回調函數中訪問這個變量致使觸發的getter並不須要執行依賴收集,直接返回value便可
if (Dep.target) {
dep.depend()
if (childOb) {
// 若是value在observe過程當中生成了ob實例,那麼就讓ob的dep也收集依賴
childOb.dep.depend()
}
if (isArray(value)) {
//若是數組元素也是對象,那麼他們observe過程也生成了ob實例
dependArray(value)
}
}
return value
},
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val
if (newVal === value) {
return
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// observe 這個新數據
childOb = observe(newVal)
// 通知到dep 進行數據更新。
dep.notify()
}
})複製代碼
到這個時候,Observer 監聽已經完成,若是數據更新,咱們會進行 dep.notify()
方法:
dep.notify() , 方法裏會執行update()
:
update() , 中會進行queueWatcher()
方法:
queueWatcher()
, 目的是經過 nextTicker
來執行 run()
:
在 run()
裏,其實就是執行 this.get()
方法:
在 get()
方法裏,會運行 this.getter()
, 方法來更新 DOM 。
this.getter()
方法是啥?
再就是上文中所提到的new Watcher(), 的第二個參數。
vm._watcher = new Watcher(vm, () => {
vm._update(vm._render(), hydrating)
}, noop)複製代碼
而在 new Watcher
構造完成後,會調用 this.get()
,觸發 this.getter()
,方法觸發 DOM 更新。
具體能夠看watcher.js:
截一個關鍵代碼部分的圖:
以上,用一張序列圖來表示,既就是:
對以上總結:
_init
,對屬性利用 Object.defineProperty
,將屬性轉爲 getter/setter
,在 setter
方法裏,會調用 dep.notify()
。vm
設置 new Watcher
。data
變化時,進行數據跟新。完。