數(shù)據(jù)驅(qū)動(dòng)渲染(二)

回憶

????????這里我們將對(duì)render函數(shù)把template轉(zhuǎn)化成vnode的過(guò)程進(jìn)行介紹贸弥。

????????Vue.prototype._render方法中調(diào)用vm.$options.render.call(vm_renderProxy, vm.$creatElement)窟坐,$options.render本身是個(gè)函數(shù),以creatElement方法為參數(shù)绵疲。傳入的參數(shù)vm.$creatElement是個(gè)function createElement(vm,a,x,c,d,true)函數(shù)哲鸳。createElement最終會(huì)調(diào)用??_createElement。

????????轉(zhuǎn)化的最終方法為_createElement(context(VNode 的上下文環(huán)境,它是?Component?類型),tag(標(biāo)簽,它可以是一個(gè)字符串盔憨,也可以是一個(gè)?Component),data(VNode 的數(shù)據(jù),?VNodeData?類型),children(當(dāng)前 VNode 的子節(jié)點(diǎn)帕胆,它是任意類型的,它接下來(lái)需要被規(guī)范為標(biāo)準(zhǔn)的 VNode 數(shù)組),normalizationType(子節(jié)點(diǎn)規(guī)范的類型般渡,類型不同規(guī)范的方法也就不一樣懒豹,它主要是參考?render?函數(shù)是編譯生成的還是用戶手寫的))芙盘。

????????主要邏輯分為兩個(gè)部分?children?的規(guī)范化以及 VNode 的創(chuàng)建

? ?????1脸秽、?children?的規(guī)范化儒老,會(huì)調(diào)用simpleNormalizeChildren或者normalizeChildren把children由樹狀結(jié)構(gòu)打平成一維數(shù)組。2记餐、通過(guò)vnode=new VNode()創(chuàng)建vnode驮樊。

render

core/instance/lifecyle.js

????????Vue 的?_render?方法是實(shí)例的一個(gè)私有方法,它用來(lái)把實(shí)例渲染成一個(gè)虛擬 Node片酝。它的定義在src/core/instance/render.js文件中囚衔。

1、wm._renderProxy在生產(chǎn)環(huán)境就是vm(this本身)雕沿,在生產(chǎn)環(huán)境是proxy對(duì)象练湿。2、vm.$creatElement是在initRender函數(shù)中定義(在init初始化時(shí)會(huì)執(zhí)行)审轮。

initRender函數(shù)主要部分($creatElement)

對(duì)手寫render和編譯生成render分別執(zhí)行creatElement()肥哎。手寫render函數(shù)和直接在template函數(shù)寫編譯出來(lái)render,渲染過(guò)程不一樣疾渣。1篡诽、直接在template函數(shù)寫的話會(huì)在執(zhí)行newVue之前先把template內(nèi)容渲染出來(lái),再執(zhí)行newVue()的mounted時(shí)把template內(nèi)的插值{{...}}替換成真實(shí)數(shù)據(jù)榴捡。2杈女、直接通過(guò)純r(jià)ender函數(shù),不用在頁(yè)面上顯示插值{{..}}吊圾,而在render函數(shù)執(zhí)行完畢后达椰,直接把message寫上去,也不會(huì)再執(zhí)行把template轉(zhuǎn)化成render函數(shù)這一步街夭,用戶體驗(yàn)更好砰碴。

? ??????最關(guān)鍵的是?render?方法的調(diào)用,我們?cè)谄綍r(shí)的開發(fā)工作中手寫?render?方法的場(chǎng)景比較少板丽,而寫的比較多的是?template?模板呈枉,在之前的?mounted?方法的實(shí)現(xiàn)中,會(huì)把?template?編譯成?render?方法埃碱,但這個(gè)編譯過(guò)程是非常復(fù)雜的猖辫,我們不打算在這里展開講,之后會(huì)專門花一個(gè)章節(jié)來(lái)分析 Vue 的編譯過(guò)程砚殿。

????????可以看到啃憎,render?函數(shù)中的?createElement?方法就是?vm.$createElement?方法。

????????實(shí)際上似炎,vm.$createElement?方法定義是在執(zhí)行?initRender?方法的時(shí)候辛萍,可以看到除了?vm.$createElement方法悯姊,還有一個(gè)?vm._c?方法,它是被模板編譯成的?render?函數(shù)使用贩毕,而vm.$createElement?是用戶手寫?render?方法使用的悯许, 這倆個(gè)方法支持的參數(shù)相同,并且內(nèi)部都調(diào)用了?createElement?方法辉阶。

render渲染實(shí)際步驟

? ???????在 Vue 的官方文檔中介紹了?render?函數(shù)的第一個(gè)參數(shù)是?createElement先壕,那么結(jié)合之前的例子。

template

相當(dāng)于我們編寫如下?render?函數(shù):

render函數(shù)

再回到?_render?函數(shù)中的?render?方法的調(diào)用:? ? ? ??

調(diào)用

繼續(xù)看看vm._renderProxy谆甜,定義在instance/init.js中

我們繼續(xù)往下找垃僚,instance/proxy中

做對(duì)象訪問(wèn)劫持,如果沒(méi)取到就報(bào)錯(cuò)

????????看完render渲染函數(shù) vnode = render.call(vm._renderProxy, vm.$createElement),我們接著往下看规辱。

判斷dom根節(jié)點(diǎn)是否只有一個(gè)Vnode谆棺,否就報(bào)錯(cuò)。最后返回一個(gè)Vnode

????????vm._render?最終是通過(guò)執(zhí)行?createElement?方法并返回的是?vnode按摘,它是一個(gè)虛擬 Node包券。Vue 2.0 相比 Vue 1.0 最大的升級(jí)就是利用了 Virtual DOM纫谅。因此在分析?createElement?的實(shí)現(xiàn)前炫贤,我們先了解一下 Virtual DOM 的概念。

Virtual DOM

????????Virtual DOM 這個(gè)概念相信大部分人都不會(huì)陌生付秕,它產(chǎn)生的前提是瀏覽器中的 DOM 是很“昂貴"的兰珍,為了更直觀的感受,可以簡(jiǎn)單的把一個(gè)簡(jiǎn)單的 div 元素的屬性都打印出來(lái)询吴,很多屬性非常龐大掠河,因?yàn)闉g覽器的標(biāo)準(zhǔn)就把 DOM 設(shè)計(jì)的非常復(fù)雜。當(dāng)我們頻繁的去做 DOM 更新猛计,會(huì)產(chǎn)生一定的性能問(wèn)題唠摹。

????????而 Virtual DOM 就是用一個(gè)原生的 JS 對(duì)象去描述一個(gè) DOM 節(jié)點(diǎn),所以它比創(chuàng)建一個(gè) DOM 的代價(jià)要小很多奉瘤。在 Vue.js 中勾拉,Virtual DOM 是用?VNode?這么一個(gè) Class 去描述,它是定義在src/core/vdom/vnode.js中的盗温。

????????Vue.js 中的 Virtual DOM 的定義還是略微復(fù)雜一些的藕赞,因?yàn)樗@里包含了很多 Vue.js 的特性。這里千萬(wàn)不要被這些茫茫多的屬性嚇到卖局,實(shí)際上 Vue.js 中 Virtual DOM 是借鑒了一個(gè)開源庫(kù)snabbdom的實(shí)現(xiàn)斧蜕,然后加入了一些 Vue.js 特色的東西。我建議大家如果想深入了解 Vue.js 的 Virtual DOM 前不妨先閱讀這個(gè)庫(kù)的源碼砚偶,因?yàn)樗雍?jiǎn)單和純粹批销。

? ??????其實(shí) VNode 是對(duì)真實(shí) DOM 的一種抽象描述洒闸,它的核心定義無(wú)非就幾個(gè)關(guān)鍵屬性,標(biāo)簽名均芽、數(shù)據(jù)顷蟀、子節(jié)點(diǎn)、鍵值等骡技,其它屬性都是都是用來(lái)擴(kuò)展 VNode 的靈活性以及實(shí)現(xiàn)一些特殊 feature 的鸣个。由于 VNode 只是用來(lái)映射到真實(shí) DOM 的渲染,不需要包含操作 DOM 的方法布朦,因此它是非常輕量和簡(jiǎn)單的囤萤。

????????Virtual DOM 除了它的數(shù)據(jù)結(jié)構(gòu)的定義,映射到真實(shí)的 DOM 實(shí)際上要經(jīng)歷 VNode 的 create是趴、diff涛舍、patch 等過(guò)程。那么在 Vue.js 中唆途,VNode 的 create?是通過(guò)之前提到的?createElement?方法創(chuàng)建的富雅,我們接下來(lái)分析這部分的實(shí)現(xiàn)。

createElement

????????接下來(lái)分析這個(gè)creatElement肛搬, Vue.js 利用 createElement 方法創(chuàng)建 VNode没佑,它定義在src/core/vdom/create-elemenet.js中。

參數(shù)

????????_createElement?方法有 5 個(gè)參數(shù)温赔,context?表示 VNode 的上下文環(huán)境蛤奢,它是?Component?類型;tag?表示標(biāo)簽陶贼,它可以是一個(gè)字符串啤贩,也可以是一個(gè)?Component;data?表示 VNode 的數(shù)據(jù)拜秧,它是一個(gè)?VNodeData?類型痹屹,可以在?flow/vnode.js?中找到它的定義,這里先不展開說(shuō)枉氮;children表示當(dāng)前 VNode 的子節(jié)點(diǎn)志衍,它是任意類型的,它接下來(lái)需要被規(guī)范為標(biāo)準(zhǔn)的 VNode 數(shù)組嘲恍;normalizationType表示子節(jié)點(diǎn)規(guī)范的類型足画,類型不同規(guī)范的方法也就不一樣,它主要是參考?render?函數(shù)是編譯生成的還是用戶手寫的佃牛。

????????createElement?函數(shù)的流程略微有點(diǎn)多淹辞,我們接下來(lái)主要分析 2 個(gè)重點(diǎn)的流程 ——?children?的規(guī)范化以及 VNode 的創(chuàng)建

children規(guī)范化
規(guī)范化詳細(xì)
遞歸打平children

children 的規(guī)范化

????????由于 Virtual DOM 實(shí)際上是一個(gè)樹狀結(jié)構(gòu)俘侠,每一個(gè) VNode 可能會(huì)有若干個(gè)子節(jié)點(diǎn)象缀,這些子節(jié)點(diǎn)應(yīng)該也是 VNode 的類型蔬将。_createElement?接收的第 4 個(gè)參數(shù) children 是任意類型的,因此我們需要把它們規(guī)范成 VNode 類型央星。

????????這里根據(jù)?normalizationType?的不同霞怀,調(diào)用了?normalizeChildren(children)?和?simpleNormalizeChildren(children)?方法,它們的定義都在src/core/vdom/helpers/normalzie-children.js?

? ??????simpleNormalizeChildren?方法調(diào)用場(chǎng)景是?render?函數(shù)當(dāng)函數(shù)是編譯生成的莉给。理論上編譯生成的?children?都已經(jīng)是 VNode 類型的毙石,但這里有一個(gè)例外就是?functional component?函數(shù)式組件返回的是一個(gè)數(shù)組而不是一個(gè)根節(jié)點(diǎn)颓遏,所以會(huì)通過(guò)?Array.prototype.concat?方法把整個(gè)?children?數(shù)組打平徐矩,讓它的深度只有一層。

? ???????normalizeChildren方法的調(diào)用場(chǎng)景有 2 種叁幢,一個(gè)場(chǎng)景是?render?函數(shù)是用戶手寫的滤灯,當(dāng)?children?只有一個(gè)節(jié)點(diǎn)的時(shí)候,Vue.js 從接口層面允許用戶把?children?寫成基礎(chǔ)類型用來(lái)創(chuàng)建單個(gè)簡(jiǎn)單的文本節(jié)點(diǎn)曼玩,這種情況會(huì)調(diào)用?createTextVNode?創(chuàng)建一個(gè)文本節(jié)點(diǎn)的 VNode鳞骤;另一個(gè)場(chǎng)景是當(dāng)編譯?slot、v-for?的時(shí)候會(huì)產(chǎn)生嵌套數(shù)組的情況黍判,會(huì)調(diào)用?normalizeArrayChildren?方法豫尽,接下來(lái)看一下它的實(shí)現(xiàn)。

? ??????normalizeArrayChildren接收 2 個(gè)參數(shù)样悟,children?表示要規(guī)范的子節(jié)點(diǎn)拂募,nestedIndex?表示嵌套的索引庭猩,因?yàn)閱蝹€(gè)?child?可能是一個(gè)數(shù)組類型窟她。?normalizeArrayChildren?主要的邏輯就是遍歷?children,獲得單個(gè)節(jié)點(diǎn)?c蔼水,然后對(duì)?c?的類型判斷震糖,如果是一個(gè)數(shù)組類型,則遞歸調(diào)用?normalizeArrayChildren; 如果是基礎(chǔ)類型趴腋,則通過(guò)?createTextVNode?方法轉(zhuǎn)換成 VNode 類型吊说;否則就已經(jīng)是 VNode 類型了,如果?children?是一個(gè)列表并且列表還存在嵌套的情況优炬,則根據(jù)?nestedIndex?去更新它的 key颁井。這里需要注意一點(diǎn),在遍歷的過(guò)程中蠢护,對(duì)這 3 種情況都做了如下處理:如果存在兩個(gè)連續(xù)的?text?節(jié)點(diǎn)雅宾,會(huì)把它們合并成一個(gè)?text?節(jié)點(diǎn)。

? ??????經(jīng)過(guò)對(duì)?children?的規(guī)范化葵硕,children?變成了一個(gè)類型為 VNode 的 Array眉抬。

創(chuàng)建Vnode

VNode 的創(chuàng)建

????????回到?createElement?函數(shù)贯吓,規(guī)范化?children?后,接下來(lái)會(huì)去創(chuàng)建一個(gè) VNode 的實(shí)例蜀变。

????????這里先對(duì)?tag?做判斷悄谐,如果是?string?類型,則接著判斷如果是內(nèi)置的一些節(jié)點(diǎn)库北,則直接創(chuàng)建一個(gè)普通 VNode爬舰,如果是為已注冊(cè)的組件名菲茬,則通過(guò)?createComponent?創(chuàng)建一個(gè)組件類型的 VNode庆锦,否則創(chuàng)建一個(gè)未知的標(biāo)簽的 VNode挠日。 如果是?tag?一個(gè)?Component?類型障簿,則直接調(diào)用?createComponent?創(chuàng)建一個(gè)組件類型的 VNode 節(jié)點(diǎn)轮蜕。對(duì)于?createComponent?創(chuàng)建組件類型的 VNode 的過(guò)程橘券,我們之后會(huì)去介紹榨崩,本質(zhì)上它還是返回了一個(gè) VNode七问。

? ??????那么至此颈墅,我們大致了解了?createElement?創(chuàng)建 VNode 的過(guò)程蜡镶,每個(gè) VNode 有?children,children?每個(gè)元素也是一個(gè) VNode恤筛,這樣就形成了一個(gè) VNode Tree官还,它很好的描述了我們的 DOM Tree。

????????回到?mountComponent?函數(shù)的過(guò)程毒坛,我們已經(jīng)知道?vm._render?是如何創(chuàng)建了一個(gè) VNode望伦,接下來(lái)就是要把這個(gè) VNode 渲染成一個(gè)真實(shí)的 DOM 并渲染出來(lái),這個(gè)過(guò)程是通過(guò)?vm._update?完成的煎殷,接下來(lái)分析一下這個(gè)過(guò)程屯伞。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市豪直,隨后出現(xiàn)的幾起案子劣摇,更是在濱河造成了極大的恐慌,老刑警劉巖弓乙,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件末融,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡暇韧,警方通過(guò)查閱死者的電腦和手機(jī)勾习,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)懈玻,“玉大人巧婶,你說(shuō)我怎么就攤上這事。” “怎么了粹舵?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵钮孵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我眼滤,道長(zhǎng)巴席,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任诅需,我火速辦了婚禮漾唉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘堰塌。我一直安慰自己赵刑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布场刑。 她就那樣靜靜地躺著般此,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牵现。 梳的紋絲不亂的頭發(fā)上铐懊,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音瞎疼,去河邊找鬼科乎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛贼急,可吹牛的內(nèi)容都是我干的茅茂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼太抓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼空闲!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起腻异,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤进副,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后悔常,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡给赞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年机打,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片片迅。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡残邀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芥挣,我是刑警寧澤驱闷,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站空免,受9級(jí)特大地震影響空另,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蹋砚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一扼菠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坝咐,春花似錦循榆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至泽篮,卻和暖如春浦楣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咪辱。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工振劳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人油狂。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓历恐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親专筷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子弱贼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 回憶 首先,render函數(shù)中手寫h=>h(app)磷蛹,new Vue()實(shí)例初始化init()和原來(lái)一樣吮旅。$mou...
    LoveBugs_King閱讀 2,281評(píng)論 1 2
  • 這篇筆記主要包含 Vue 2 不同于 Vue 1 或者特有的內(nèi)容,還有我對(duì)于 Vue 1.0 印象不深的內(nèi)容味咳。關(guān)于...
    云之外閱讀 5,050評(píng)論 0 29
  • 一庇勃、Native 每一個(gè)頁(yè)面都是一個(gè) instance,framework 是全局唯一的槽驶,js 和 native ...
    Yang152412閱讀 5,666評(píng)論 0 50
  • 這個(gè)介紹的是 createElement 是怎么創(chuàng)建一個(gè)元素的责嚷。接下來(lái)還有一篇會(huì)介紹到 createElement...
    阿暢_閱讀 23,455評(píng)論 0 3
  • 今天,還是那個(gè)我還是那個(gè)循規(guī)蹈矩掂铐。 今天我又和穆蕊蕊罕拂,還有他的奶奶一起去了鄉(xiāng)下揍异。 ...
    谷殿艷閱讀 92評(píng)論 0 0