Vue源碼解析(2)-$mount實(shí)現(xiàn)
上一節(jié),我們講到了在創(chuàng)建一個(gè)vu實(shí)例時(shí)候,vue的構(gòu)造函數(shù)會(huì)執(zhí)行this._init()函數(shù),然后啟動(dòng)函數(shù)的最后會(huì)執(zhí)行_init方法里面執(zhí)行vm.$mount(vm.$options.el),將實(shí)例掛載在dom上,至此啟動(dòng)函數(shù)結(jié)束??梢钥闯鰒ue.$mount為vue render主要函數(shù)。
本節(jié)我們會(huì)主要著重于vue整個(gè)render過(guò)程,過(guò)于細(xì)節(jié)化的東西會(huì)再以后章節(jié)詳細(xì)描述。
首先我們通過(guò)流程圖看下vue獨(dú)立構(gòu)建渲染過(guò)程:

為了了解vue渲染過(guò)程我們必須知道vue有幾種構(gòu)建方式:
1 獨(dú)立構(gòu)建 (包含template編譯器)
渲染過(guò)程:html字符串 → render函數(shù) → vnode → 真實(shí)dom節(jié)點(diǎn)
2 運(yùn)行中構(gòu)建 (不包含template編譯器)
渲染過(guò)程: render函數(shù) → vnode → 真實(shí)dom節(jié)點(diǎn)產(chǎn)生兩種構(gòu)建方式原因是由于歷史版本中vue1.0中編譯過(guò)程依賴瀏覽器的dom,無(wú)法將編譯器和運(yùn)行中分開,編譯器和運(yùn)行中是打包在一起的,都在瀏覽器中執(zhí)行,但是在vue2.0中,為了支持服務(wù)端渲染,必須將編譯器和運(yùn)行時(shí)分開,就形成了獨(dú)立構(gòu)建(編譯器+運(yùn)行時(shí))和運(yùn)行時(shí)構(gòu)建,可以看到運(yùn)行時(shí)構(gòu)建打包出來(lái)必定比獨(dú)立構(gòu)建中小的多。
綜上所述,我們說(shuō)了這么多,接下來(lái)我們來(lái)看下vue源碼的分析過(guò)程。
Vue 中我們是通過(guò)$mount實(shí)例方法去掛載vm的,$mount 方法在多個(gè)文件中都有定義,如src/platform/web/entry-runtime-with-complier.js , src/platform/web/runtime/index.js src/platform/weex/runtime/index.js 因?yàn)?mount 這個(gè)方法的實(shí)現(xiàn)是和平臺(tái)、構(gòu)建方式都相關(guān)的。接下來(lái)我們分析運(yùn)行時(shí)版本的$mount 實(shí)現(xiàn),因?yàn)閽侀_ webpack 的 vue-loader,我們?cè)诩兦岸藶g覽器環(huán)境分析 Vue 的工作原理 。
在src/platforms/web/entry-runtime-with-compiler,$mount函數(shù)在vue的原型上被定義

可以看到el可以是字符串也可以是個(gè)element,調(diào)用了query方法(platfomrs/web/util/index.js):

可以看到當(dāng)傳入字符串時(shí)候回篩選出字符對(duì)應(yīng)的dom節(jié)點(diǎn),如果是dom節(jié)點(diǎn)話直接返回
至此,el就轉(zhuǎn)化為dom對(duì)象
然后做了一個(gè)判斷,判斷el不能是body以及html,為了防止覆蓋截下來(lái)就是判斷是否有render函數(shù)以及template,若vue實(shí)例中沒有render,則將template編譯成render,也就是說(shuō)vue只認(rèn)render函數(shù),同時(shí),因?yàn)閠emplate可以寫成多掙形式,因此el也會(huì)轉(zhuǎn)換成template(使用getOutHTML函數(shù)),再轉(zhuǎn)換成render。本段不好描述邏輯,故代碼加備注

在getOuterHTML中,主要是獲得el所對(duì)應(yīng)的dom及其內(nèi)容

最后,調(diào)用mount函數(shù)完成渲染。

其中這個(gè)mount指的是 platform/runtime/index.js里面的方法

上面的方法主要是做一些判斷,并且執(zhí)行mountcomponent方法,mountComponent核心就是先調(diào)用vm._render方法先生成虛擬 Node,再實(shí)例化一個(gè)渲染W(wǎng)atcher,在它的回調(diào)函數(shù)中會(huì)調(diào)用updateComponent方法,最終調(diào)用vm._update更新 DOM。那我們來(lái)看看這個(gè)方法是在core/instance/lifecycle里面:
里面的邏輯主要是判斷是否有render函數(shù),如果沒有,執(zhí)行createEmptyVNode,并且假如當(dāng)我們使用了runtime-only版本,我們又使用template語(yǔ)法,不是直接寫render函數(shù),還有一種情況就是既沒有寫render函數(shù)和template函數(shù)會(huì)報(bào)錯(cuò):
vm.$options.template && vm.$options.template.charAt(0) !== #) || vm.$options.el || el會(huì)報(bào)出警告,

最后定義updateComponent函數(shù),該函數(shù)完成的功能是渲染和更新。至于什么時(shí)候調(diào)用這個(gè)函數(shù),這個(gè)過(guò)程是使用觀察者模式,由wacher一起完成的。new watcher第一個(gè)參數(shù)當(dāng)前vm實(shí)例,第三個(gè)函數(shù)是空函數(shù),第四個(gè)參數(shù)是個(gè)配置,第五個(gè)參數(shù)是布爾值。在src/core/observer/watach.js中定義了Watcher類,在構(gòu)造函數(shù)中,傳入的參數(shù)中有一個(gè)是否為渲染watcher,如果是,則將初始化實(shí)例的_watche,Watcher在這里起到兩個(gè)作用,一個(gè)是初始化的時(shí)候會(huì)執(zhí)行回調(diào)函數(shù),另一個(gè)是當(dāng) vm 實(shí)例中的監(jiān)測(cè)的數(shù)據(jù)發(fā)生變化的時(shí)候執(zhí)行回調(diào)函數(shù),函數(shù)最后判斷為根節(jié)點(diǎn)的時(shí)候設(shè)置vm._isMounted為true, 表示這個(gè)實(shí)例已經(jīng)掛載了,同時(shí)執(zhí)行mounted鉤子函數(shù)。 這里注意vm.$vnode表示 Vue 實(shí)例的父虛擬 Node,所以它為Null則表示當(dāng)前是根 Vue 的實(shí)例。


將getter賦值給expOrFn,這里傳給expOrFn的是updateComponent,隨后,調(diào)用get(),在get函數(shù)中,主要是收集一些依賴,然后在初始化或者有更新時(shí),調(diào)用this.getter(對(duì)應(yīng)著updateComponent函數(shù)),在$mount中,首先是根據(jù)template將其轉(zhuǎn)換為render(在沒有render的情況下),然后調(diào)用mountComponent函數(shù),該函數(shù)主要調(diào)用的是updateComponent函數(shù)。updateComponent函數(shù)使用渲染watcher,在初次渲染或者值發(fā)生改變的時(shí)候調(diào)用該函數(shù)(這個(gè)過(guò)程由wacher完成),使用了觀察者模式。

看到這里大家可能有一些迷糊,因?yàn)槲覀兩鲜鲋皇呛?jiǎn)單把$mount的過(guò)程簡(jiǎn)單介紹,并沒有對(duì)render函數(shù)的編譯過(guò)程做具體的介紹。那么 render函數(shù)的編譯過(guò)程是怎么樣的?
render函數(shù)的編譯的主要幾個(gè)步驟,這可以幫助我們從整體上把握它:
1 給編譯options添加web平臺(tái)特性2 將template字符串解析成ast
3 優(yōu)化:將那些不會(huì)被改變的節(jié)點(diǎn)(statics)打上標(biāo)記
4 生成render函數(shù)字符串,并用with包裹(最新版本有改為buble)
5 通過(guò)new Function的方式生成render函數(shù)并緩存
本章只是簡(jiǎn)單介紹render函數(shù)過(guò)程,以后會(huì)有專門章節(jié)介紹render函數(shù)具體編譯步驟。
下節(jié),我們會(huì)繼續(xù)介紹 render函數(shù)編譯的5個(gè)步驟具體源碼實(shí)現(xiàn)分析。敬請(qǐng)期待.....
掃描二維碼推送至手機(jī)訪問(wèn)。
版權(quán)聲明:本文由財(cái)神資訊-領(lǐng)先的體育資訊互動(dòng)媒體轉(zhuǎn)載發(fā)布,如需刪除請(qǐng)聯(lián)系。