摘要:表示虛擬節(jié)點(diǎn),為什么叫虛擬節(jié)點(diǎn)呢,因?yàn)椴皇钦娴墓?jié)點(diǎn)。因?yàn)槭菍?duì)象,不管還是瀏覽器,都可以統(tǒng)一操作,從而獲得了服務(wù)端渲染原生渲染手寫渲染函數(shù)等能力減少操作。
寫文章不容易,點(diǎn)個(gè)贊唄兄弟
專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧
研究基于 Vue版本 【2.5.17】
如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧
【Vue原理】VNode - 源碼版
今天就來(lái)探索 VNode 的源碼,VNode 是 Vue2 渲染機(jī)制中很重要的一部分,是深入Vue 必須了解的部分
我們以4個(gè)問(wèn)題來(lái)開始我們的探索
1、vnode 是什么及其作用 2、vnode 什么時(shí)候生成 3、vnode 怎么生成 4、vnode 存放什么信息 5、vnode 存放在哪里
文章很長(zhǎng),看之前值做好準(zhǔn)備
VNode是什么及作用首先,第一個(gè)問(wèn)題已經(jīng)很爛了,網(wǎng)上有很多相關(guān)的內(nèi)容,為了內(nèi)容的完整性,所以也放上來(lái)哈哈。
VNode 表示 虛擬節(jié)點(diǎn) Virtual DOM,為什么叫虛擬節(jié)點(diǎn)呢,因?yàn)椴皇钦娴?DOM 節(jié)點(diǎn)。
他只是用 javascript 對(duì)象來(lái)描述真實(shí) DOM,這么描述,把DOM標(biāo)簽,屬性,內(nèi)容都變成 對(duì)象的屬性
就像用 JavaScript 對(duì)象描述一個(gè)人一樣
{sex:"男", name:"神仙朱", salary:5000,children:null}
過(guò)程就是,把你的 template 模板 描述成 VNode,然后一系列操作之后通過(guò) VNode 形成真實(shí)DOM進(jìn)行掛載
是什么?
JavaScript 對(duì)象
什么用?
1、兼容性強(qiáng),不受執(zhí)行環(huán)境的影響。VNode 因?yàn)槭?JS 對(duì)象,不管 Node 還是 瀏覽器,都可以統(tǒng)一操作, 從而獲得了服務(wù)端渲染、原生渲染、手寫渲染函數(shù)等能力
2、減少操作 DOM。任何頁(yè)面的變化,都只使用 VNode 進(jìn)行操作對(duì)比,只需要在最后一步掛載更新DOM,不需要頻繁操作DOM,從而提高頁(yè)面性能
VNode怎么生成在 Vue 源碼中,vnode 是通過(guò)一個(gè)構(gòu)造函數(shù)生成的,構(gòu)造函數(shù)看起來(lái)挺簡(jiǎn)單的
本來(lái)以為很多內(nèi)容,帶著沉重的心情去探索,然后看到之后就放松了下來(lái),看了一會(huì),心情再次沉重了起來(lái)
其中涉及的內(nèi)容還是挺多的....不然哪里來(lái)開篇的那么多問(wèn)題
行了,看下 VNode 的構(gòu)造函數(shù)
function VNode( tag, data, children, text, elm, context, componentOptions ) { this.tag = tag; // 標(biāo)簽名 this.data = data; this.children = children; // 子元素 this.text = text; // 文本內(nèi)容 this.elm = elm; // Dom 節(jié)點(diǎn) this.context = context; this.componentOptions = componentOptions; this.componentInstance = undefined; this.parent = undefined; this.isStatic = false; // 是否靜態(tài)節(jié)點(diǎn) this.isComment = false; // 是否是注釋節(jié)點(diǎn) this.isCloned = false; // 是否克隆節(jié)點(diǎn) };
看完上面,先不要糾結(jié)都是什么東西,先來(lái)走一遍
比如我們使用 vnode 去描述這樣一個(gè)template
111111
使用 VNode 構(gòu)造函數(shù)就可以生成下面的 VNode
{ tag: "div", data: { attrs:{href:"2222"} staticClass: "parent", staticStyle: { height: "0" } }, children: [{ tag: undefined, text: "111111" }] }
這個(gè) JS 對(duì)象,就已經(jīng)囊括了整個(gè)模板的所有信息,完全可以根據(jù)這個(gè)對(duì)象來(lái)構(gòu)造真實(shí)DOM了
至于其中都是什么意思,請(qǐng)看下個(gè)問(wèn)題
VNode存放什么信息新建一個(gè) vnode 的時(shí)候,包含了非常多的屬性,每個(gè)屬性都是節(jié)點(diǎn)的描述的一部分
我們只撿一些屬性來(lái)探索一下,了解主體即可
普通屬性1、data
1、存儲(chǔ)節(jié)點(diǎn)的屬性,class,style 等
2、存儲(chǔ)綁定的事件
3、....其他
2、elm
真實(shí)DOM 節(jié)點(diǎn)
生成VNode 的時(shí)候,并不存在真實(shí) DOM
elm 會(huì)在需要?jiǎng)?chuàng)建DOM 時(shí)完成賦值,具體函數(shù)在 createElm 中
賦值語(yǔ)句就是一句(簡(jiǎn)化了源碼)
3、context
渲染這個(gè)模板的上下文對(duì)象
意思就是,template 里面的動(dòng)態(tài)數(shù)據(jù)要從這個(gè) context 中獲取,而 context 就是 Vue 實(shí)例
如果是頁(yè)面,那么context 就是本頁(yè)面的實(shí)例,如果是組件,context則是組件的實(shí)例
4 isStatic
是否是靜態(tài)節(jié)點(diǎn)
當(dāng)一個(gè)節(jié)點(diǎn)被標(biāo)記為靜態(tài)節(jié)點(diǎn)的時(shí)候,說(shuō)明這個(gè)節(jié)點(diǎn)可以不用去更新它了,當(dāng)數(shù)據(jù)變化的時(shí)候,可以忽略去比對(duì)他,以提高比對(duì)效率
組件相關(guān)屬性
1、parent
這個(gè)parent 表示是組件的外殼節(jié)點(diǎn)
額,什么是外殼節(jié)點(diǎn),舉個(gè)栗子先吧
1、存在這樣一個(gè)組件 test
2、頁(yè)面中使用這個(gè)組件
誒,到這里就有意思了,組件其實(shí)應(yīng)有兩種 VNode
這兩種VNode 名義上都是對(duì)的,都有理,誰(shuí)是正牌不好說(shuō)
最后尤大判定第一個(gè) VNode 是 第二個(gè) VNode 的爸爸,也就是外殼節(jié)點(diǎn)
外殼節(jié)點(diǎn)通常是 父組件和 子組件的 關(guān)聯(lián),用于保存一些父組件傳給子組件的數(shù)據(jù) 等
2 componentInstance
這個(gè)顧名思義,就是組件生成的實(shí)例,保存在這里
上面 test 組件的外殼節(jié)點(diǎn)中的 componentInstance
3 componentOptions
這個(gè)就存儲(chǔ)一些 父子組件 PY 交易的證據(jù)
比如 props,事件,slot 什么的,打印看下
其中 children 保存的就是 slot,listeners 保存 事件,propsData 保存 props
VNode怎么生成在初始化完選項(xiàng),解析完模板之后,就需要掛載 DOM了。此時(shí)就需要生成 VNode,才能根據(jù) VNode 生成 DOM 然后掛載
掛載 DOM 第一步,就是先執(zhí)行渲染函數(shù),得到整個(gè)模板的 VNode
比如有以下渲染函數(shù),執(zhí)行會(huì)返回 VNode,就是 _c 返回的VNode
function (){ with(this){ return _c("div",{attrs:{"href":"xxxx"}},["1111"]). } }
渲染函數(shù)會(huì)綁定上下文對(duì)象,加上 with 的作用,_c 其實(shí)就是 vm._c
現(xiàn)在就來(lái)看 vm._c 是什么東西
vm._c = function(a, b, c, d) { return createElement(vm, a, b, c, d, false); };
function createElement( context, tag, data, children, normalizationType ) { var vnode; if (tag是正常html標(biāo)簽) { vnode = new VNode( tag, data, children, undefined, undefined, context ); } else if (tag 是組件) { vnode = createComponent( Ctor, data, context, children, tag ); } return vnode }
我們可以看到,正常標(biāo)簽 和 組件會(huì)走不同流程
1 、正常標(biāo)簽
比如有這樣一個(gè)正常標(biāo)簽?zāi)0?/p>
解析成渲染函數(shù)如下
function (){ with(this){ return _c("div",{ attrs:{"href":"xxxx"}}, ["1111"] ) } }
看上面_c 源碼,可以知道經(jīng)過(guò) _c 把參數(shù)傳導(dǎo),這樣去構(gòu)建 VNode
new VNode(tag, data, children, undefined, undefined, context);
這樣就保存了 tag,data,children 和 context
2、組件
比如頁(yè)面使用了test組件
解析成渲染函數(shù)如下
with(this){ return _c("div",[ _c("test", {attrs:{"name":name}}, ["1111"] ) ],1) }
看上面 _c 代碼知道 ,_c 會(huì)先調(diào)用 createComponent
createComponent(Ctor, data, context, children, tag);}
createComponent 中也會(huì)調(diào)用 VNode 構(gòu)造函數(shù),生成VNode 并返回
function createComponent( Ctor, data, context, children, tag ) { // extractPropsFromVNodeData 作用是把傳入data的 attr 中屬于 props的篩選出來(lái) var propsData = extractPropsFromVNodeData(data, Ctor, tag); var vnode = new VNode( ("vue-component-" + (Ctor.cid) + tag), data, undefined, undefined, undefined, context, { Ctor: Ctor, // 父組件給子組件綁定的props propsData: propsData, // 父組件給子組件綁定的事件 listeners: listeners, tag: tag, children: children }); return vnode }VNode存放在哪里
那么創(chuàng)建出來(lái)的 VNode 是否有被存起來(lái),毫無(wú)疑問(wèn),肯定是要的啊
主要是三個(gè)位置存了 vnode,分別是
parent ,_vnode ,$vnode
parent 上面已經(jīng)說(shuō)過(guò),就先不提了,剩下兩個(gè)全部是掛在 Vue 實(shí)例一級(jí)屬性上的
打印一下組件的實(shí)例,可以很清楚看到這兩個(gè)屬性
下面來(lái)說(shuō)說(shuō)這兩個(gè)東西
1、_vnode_vnode 存放表示當(dāng)前節(jié)點(diǎn)的 VNode
什么叫當(dāng)前,也就是可以通過(guò)這個(gè)VNode 直接映射成 當(dāng)前真實(shí)DOM
他的作用是什么呢?
用來(lái)比對(duì)更新,比如你的數(shù)據(jù)變化了,此時(shí)會(huì)生成一個(gè)新的 VNode,然后再拿到保存的_vnode 對(duì)比,就可以得到最小區(qū)域,從而只用更新這部分
所以, _vnode 存放的可以說(shuō)是當(dāng)前節(jié)點(diǎn),也可以說(shuō)是舊節(jié)點(diǎn)
另外,_vnode 中保存有一個(gè) parent,這個(gè)parent 就是外殼節(jié)點(diǎn),上面說(shuō) vnode 的時(shí)候已經(jīng)說(shuō)過(guò)了
在哪里賦值?
我們來(lái)完整地走一遍流程,涉及源碼很多,但是我已經(jīng)非常精簡(jiǎn)了,大概了解個(gè)流程
function Vue() { ...初始化組件選項(xiàng)等 mountComponent() } function mountComponent() { ....解析模板,生成渲染函數(shù) // 用于生成VNode,生成DOM,掛載DOM updateComponent = function() { vm._update(vm._render()); }; // 新建 watcher,保存updateComponent為更新函數(shù),新建的時(shí)候會(huì)立即執(zhí)行一遍 new Watcher(vm, updateComponent) } function Watcher(vm, expOrFn) { this.getter = expOrFn ; this.getter() } // 執(zhí)行前面解析得到的渲染函數(shù),返回生成的 VNode Vue.prototype._render = () {} // 根據(jù)vnode,生成DOM 掛載 Vue.prototype._update = function(vnode) { var prevVnode = vm._vnode; vm._vnode = vnode; if (不存在舊節(jié)點(diǎn)) { ...使用vnode創(chuàng)建DOM并直接掛載 } else { ...存在舊節(jié)點(diǎn),開始比對(duì)舊節(jié)點(diǎn)和新節(jié)點(diǎn),然后創(chuàng)建DOM并掛載 } }2、$vnode
$vnode 只有組件實(shí)例才有,因?yàn)?$vnode 存放的是外殼節(jié)點(diǎn),頁(yè)面實(shí)例中是不存在 $vnode 的
本來(lái)也想走下流程的,無(wú)奈兜兜轉(zhuǎn)轉(zhuǎn)太多,涉及源碼更多
在哪里進(jìn)行賦值?
我就放最后一步 updateChildComponent
updateChildComponent 會(huì)在上個(gè) _vnode 提到的 vm._update 執(zhí)行流程中調(diào)用
function updateChildComponent( vm, parentVnode ) { vm.$options._parentVnode = parentVnode; vm.$vnode = parentVnode; if (vm._vnode) { vm._vnode.parent = parentVnode; } }最后
鑒于本人能力有限,難免會(huì)有疏漏錯(cuò)誤的地方,請(qǐng)大家多多包涵,如果有任何描述不當(dāng)?shù)牡胤?,歡迎后臺(tái)聯(lián)系本人,有重謝
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105388.html
摘要:寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之綁定組件事件上一篇已經(jīng) 寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于...
摘要:寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之創(chuàng)建組件今天就要開啟我 寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于...
摘要:寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之綁定標(biāo)簽事件這里的綁定 寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于...
摘要:寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之綁定組件自定義事件組件 寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于...
摘要:寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之掛載組件由這篇文章從模 寫文章不容易,點(diǎn)個(gè)贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于...
閱讀 1694·2021-10-13 09:39
閱讀 3166·2021-10-12 10:11
閱讀 558·2021-09-28 09:36
閱讀 2642·2019-08-30 15:55
閱讀 1392·2019-08-30 13:04
閱讀 635·2019-08-29 17:08
閱讀 1915·2019-08-29 14:14
閱讀 3415·2019-08-28 18:23