用vue也有很長(zhǎng)一段時(shí)間检号,用它做過(guò)移動(dòng)端項(xiàng)目 pc項(xiàng)目 SaaS平臺(tái)等等,總體來(lái)說(shuō)vue 還是相對(duì)于簡(jiǎn)單的萌腿、建立于良好的文檔和開(kāi)箱即用的腳手架, vue在前端框架的熱度一直很高, 最近正好在研究vue的源碼 畢竟學(xué)習(xí)之路基本都是學(xué)輪子 -> 看源碼 -> 造輪子 逐步精進(jìn)來(lái)的, 網(wǎng)上有非常多的vue的原理解析的文章嘉竟,可能我寫(xiě)的不算太好不過(guò)還是寫(xiě)下來(lái)作為一個(gè)積累和總結(jié)渐尿。
大概思路
vue的數(shù)據(jù)驅(qū)動(dòng)主要實(shí)現(xiàn)建立在三個(gè)對(duì)象上Dep余指、Watcher捕犬、Compiler,
Dep 主要負(fù)責(zé)依賴的收集
Watcher 主要負(fù)責(zé)Dep和Compiler之間的聯(lián)系
Compiler 可以理解為 virtual dom + patch 也就是負(fù)責(zé)視圖層的渲染
可以用個(gè)簡(jiǎn)單的思維導(dǎo)圖來(lái)說(shuō)明下大概原理
1. getter、setter
首先我們可以看到通過(guò)Object.defineProperty為vm實(shí)例定義了一個(gè)getter酵镜、setter. 我們可以將兩個(gè)分開(kāi)來(lái)講:
getter
getter: 主要是獲取到對(duì)應(yīng)的鍵值, 這里有一個(gè)步驟是先將原來(lái)的getter和setter提取出來(lái)碉碉,這一步主要是為了防止預(yù)定義的getter和setter,保證預(yù)定義getter淮韭、setter一樣能夠生效, 注意到有一步是判斷Dep.target, 有的話就收集依賴了垢粮。還有一些對(duì)于數(shù)組的處理, 這里就不細(xì)述了。
setter
setter: 在setter函數(shù)中靠粪,主要是對(duì)相同的值的攔截蜡吧,然后對(duì)于嵌套對(duì)象的重新observe,這里之所以會(huì)重新observe 是因?yàn)関ue在每次響應(yīng)了數(shù)據(jù)變化后占键,會(huì)清除掉所有的依賴昔善,因此要重新建立, 至于為什么會(huì)重新建立 我們會(huì)在下面大概說(shuō)下, 當(dāng)值發(fā)生變化時(shí),最后會(huì)觸發(fā)dep.notify(), 來(lái)觸發(fā)依賴的升級(jí).
2. 依賴的建立
在代碼中我們可以看到依賴的建立是通過(guò)dep.depend()來(lái)完成的, 那這個(gè)過(guò)程是在什么時(shí)候完成的呢
其實(shí)我們可以看到在 mountComponent中會(huì)new 一個(gè) watcher實(shí)例捞慌,還有就是有一個(gè)叫做updateComponent的函數(shù)產(chǎn)生,這一個(gè)函數(shù)的主要作用就是視圖層渲染更新, new Watcher參數(shù)中的before會(huì)觸發(fā)生命周期beforeUpdate的鉤子, 好了 接下來(lái)我們來(lái)看下new Watcher的時(shí)候都發(fā)生了些什么?
watcher 構(gòu)造器函數(shù)
我們能看到watcher在實(shí)例化的過(guò)程中會(huì)有大量屬性柬批,然后會(huì)根據(jù)是否是計(jì)算屬性來(lái)進(jìn)行判斷, 我們這里先不去管計(jì)算屬性, 直接就從最簡(jiǎn)單的data進(jìn)行入手, 會(huì)觸發(fā)watcher.get()
watcher.get
這里的getter 其實(shí)就是updateComponent 函數(shù), 在updateComponent函數(shù)中會(huì)通過(guò)virtual dom 和 patch函數(shù)來(lái)進(jìn)行視圖層的更新和渲染啸澡。在分析template建立虛擬dom的過(guò)程中需要去獲取vm的屬性, 因此會(huì)觸發(fā)vm.getter函數(shù)pushTarget() // 將當(dāng)前的watcher 實(shí)例 設(shè)置為 Dep.target
然后會(huì)觸發(fā)dep.depend()
這時(shí)候我們能看到Dep.target 其實(shí)就是當(dāng)前的watcher實(shí)例.
watcher.addDep
這邊有一個(gè)判斷是防止重復(fù)依賴產(chǎn)生氮帐,最后就是dep.addSub()
dep.addSub
最后通過(guò)subs來(lái)存儲(chǔ)watcher
3.屬性變化后 依賴通知watcher進(jìn)行update
還記得上面提到的setter嗎嗅虏?(不清楚的話,可以拉上去看下) setter會(huì)在屬性值發(fā)生變化時(shí)上沐,觸發(fā)dep.notify()
dep.notify
subs里面的都是收集到的watcher實(shí)例
watcher.update
這里我們看到會(huì)有一個(gè)queueWatcher的函數(shù), 這個(gè)其實(shí)是將當(dāng)前watcher 弄到一個(gè)tick中去皮服,這塊涉及到j(luò)s eventLoop
機(jī)制這一塊. 我們放到后續(xù)的文章去講,到最后的話, 其實(shí)是會(huì)觸發(fā)到watcher.run()
watcher.run & watcher.getAndInvoke
我們看到這個(gè)里面會(huì)有觸發(fā)watcher.get(), 翻到上面的截圖, 我們會(huì)發(fā)現(xiàn)get 會(huì)觸發(fā)updateComponent 這時(shí)候就完成了整個(gè)視圖層的更新。一些問(wèn)題
1、為什么vue在每次都要清除一下已有的一些依賴
2龄广、其實(shí)這個(gè)問(wèn)題我剛開(kāi)始也很疑惑硫眯,既然依賴已經(jīng)建立,那為什么還要清理掉又重新建立呢择同,所以我打開(kāi)了google两入,找到了這樣一個(gè)解釋
1、目前的話敲才,其實(shí)還不太理解裹纳, 下次我會(huì)找個(gè)機(jī)會(huì)做個(gè)demo 嘗試下
2、vue是如何基于event loop實(shí)現(xiàn)的nextTick?
3紧武、這一塊的話還是比較生疏剃氧,源碼我會(huì)繼續(xù)往下面看。
結(jié)語(yǔ)
其實(shí)我覺(jué)得vue的原理基本上就相當(dāng)于代理者模式+訂閱發(fā)布模式來(lái)實(shí)現(xiàn)的, 我覺(jué)得看源碼就要直奔主題阻星,先把主枝干理清除朋鞍,了解自己想要得到什么,整個(gè)過(guò)程就會(huì)清晰明了, 主要放一個(gè)自己的ts版的簡(jiǎn)單實(shí)現(xiàn)迫横,寫(xiě)的很簡(jiǎn)陋番舆,但是能夠大概看出vue的原理,有興趣的可以看下 ts版vue簡(jiǎn)易原理矾踱。
參考
1恨狈、vue core
2、Vue.js 技術(shù)揭秘 // 這個(gè)是我在看源碼后有一些不理解的問(wèn)題搜到的呛讲,寫(xiě)的非常好禾怠。
關(guān)注公眾號(hào)【grain先森】,回復(fù)關(guān)鍵詞 【18福利】贝搁,獲取為你準(zhǔn)備的年終福利吗氏,更多關(guān)鍵詞玩法期待你的探索~