c++ gdb 绑定源码_【Vue原理】VNode 源码版
↑點(diǎn)擊上方?“神仙朱”?一起研究Vue源碼吧
專注?Vue?源碼分享,文章分為白話版和?源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧
研究基于 Vue版本【2.5.17】
五一就這么過了,哎,大家快點(diǎn)進(jìn)入工作狀態(tài)哈哈
今天就來探索?VNode?的源碼,VNode?是?Vue2?渲染機(jī)制中很重要的一部分,是深入Vue?必須了解的部分
我們以4個(gè)問題來開始我們的探索
1、vnode 是什么及其作用
2、vnode 什么時(shí)候生成
3、vnode 怎么生成
4、vnode 存放什么信息
5、vnode 存放在哪里
文章很長,看之前值做好準(zhǔn)備
VNode是什么及作用
首先,第一個(gè)問題已經(jīng)很爛了,網(wǎng)上有很多相關(guān)的內(nèi)容,為了內(nèi)容的完整性,所以也放上來哈哈。
VNode?表示?虛擬節(jié)點(diǎn) Virtual DOM,為什么叫虛擬節(jié)點(diǎn)呢,因?yàn)椴皇钦娴?DOM?節(jié)點(diǎn)。
他只是用 javascript?對(duì)象來描述真實(shí)?DOM,這么描述,把DOM標(biāo)簽,屬性,內(nèi)容都變成?對(duì)象的屬性
就像用?JavaScript?對(duì)象描述一個(gè)人一樣
{sex:'男', name:'神仙朱', salary:5000,children:null}過程就是,把你的?template?模板?描述成?VNode,然后一系列操作之后通過?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。任何頁面的變化,都只使用 VNode?進(jìn)行操作對(duì)比,只需要在最后一步掛載更新DOM,不需要頻繁操作DOM,從而提高頁面性能
VNode怎么生成
在?Vue?源碼中,vnode?是通過一個(gè)構(gòu)造函數(shù)生成的,構(gòu)造函數(shù)看起來挺簡(jiǎn)單的
本來以為很多內(nèi)容,帶著沉重的心情去探索,然后看到之后就放松了下來,看了一會(huì),心情再次沉重了起來
其中涉及的內(nèi)容還是挺多的....不然哪里來開篇的那么多問題
行了,看下?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é)都是什么東西,先來走一遍
比如我們使用?vnode?去描述這樣一個(gè)template
<div class="parent" style="height:0" href="2222"> ? ?111111div>使用?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ì)象來構(gòu)造真實(shí)DOM了
至于其中都是什么意思,請(qǐng)看下個(gè)問題
VNode存放什么信息
新建一個(gè)?vnode?的時(shí)候,包含了非常多的屬性,每個(gè)屬性都是節(jié)點(diǎn)的描述的一部分
我們只撿一些屬性來探索一下,了解主體即可
1普通屬性
1data
1、存儲(chǔ)節(jié)點(diǎn)的屬性,class,style?等
2、存儲(chǔ)綁定的事件
3、....其他
2elm
真實(shí)DOM?節(jié)點(diǎn)
生成VNode?的時(shí)候,并不存在真實(shí)?DOM
elm?會(huì)在需要?jiǎng)?chuàng)建DOM 時(shí)完成賦值,具體函數(shù)在?createElm?中
賦值語句就是一句(簡(jiǎn)化了源碼)
3context
渲染這個(gè)模板的上下文對(duì)象
意思就是,template?里面的動(dòng)態(tài)數(shù)據(jù)要從這個(gè)?context?中獲取,而 context?就是?Vue?實(shí)例
如果是頁面,那么context?就是本頁面的實(shí)例,如果是組件,context則是組件的實(shí)例
4?isStatic
是否是靜態(tài)節(jié)點(diǎn)
當(dāng)一個(gè)節(jié)點(diǎn)被標(biāo)記為靜態(tài)節(jié)點(diǎn)的時(shí)候,說明這個(gè)節(jié)點(diǎn)可以不用去更新它了,當(dāng)數(shù)據(jù)變化的時(shí)候,可以忽略去比對(duì)他,以提高比對(duì)效率
2組件相關(guān)屬性
1parent
這個(gè)parent?表示是組件的外殼節(jié)點(diǎn)
額,什么是外殼節(jié)點(diǎn),舉個(gè)栗子先吧
1、存在這樣一個(gè)組件?test
2、頁面中使用這個(gè)組件
誒,到這里就有意思了,組件其實(shí)應(yīng)有兩種?VNode
頁面給 組件test? 解析成的?VNode | { ??? tag:"test", ??? children:undefined } |
組件內(nèi)部生成的?VNode | { ??? tag:"?h2", ? ? children:[{ ??? ???tag:undefined, ??? ???text:"我勒個(gè)去" ??? }] } |
這兩種VNode?名義上都是對(duì)的,都有理,誰是正牌不好說
最后尤大判定第一個(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)在就來看 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)過 _c?把參數(shù)傳導(dǎo),這樣去構(gòu)建?VNode
new VNode(tag, data, children, undefined, undefined, context);| tag | ?'div' |
| data | {attrs:{"href":"xxxx"}} |
| children | ?["1111"] |
| context | ?頁面實(shí)例 |
這樣就保存了?tag,data,children?和?context
2組件
比如頁面使用了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);}| tag | 'test' |
| data | { attrs:{"name":name} } |
| children | ?["1111"] |
| context | ?頁面父實(shí)例(畢竟這是外殼節(jié)點(diǎn),是在父實(shí)例中解析的) |
| Ctor | 組件的選項(xiàng),然后變成組件的構(gòu)造函數(shù),這里可以先不管,后面會(huì)詳細(xì)講 Component? |
createComponent 中也會(huì)調(diào)用?VNode?構(gòu)造函數(shù),生成VNode?并返回
function createComponent(
????Ctor, data, context,
????children, tag
) { ? ?
? ?// extractPropsFromVNodeData 作用是把傳入data的 attr 中屬于 props的篩選出來 ? ?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)建出來的?VNode?是否有被存起來,毫無疑問,肯定是要的啊
主要是三個(gè)位置存了?vnode,分別是
parent ,_vnode ,$vnode?
parent?上面已經(jīng)說過,就先不提了,剩下兩個(gè)全部是掛在?Vue?實(shí)例一級(jí)屬性上的
打印一下組件的實(shí)例,可以很清楚看到這兩個(gè)屬性
下面來說說這兩個(gè)東西
1_vnode
_vnode?存放表示當(dāng)前節(jié)點(diǎn)的?VNode
什么叫當(dāng)前,也就是可以通過這個(gè)VNode?直接映射成?當(dāng)前真實(shí)DOM
他的作用是什么呢?
用來比對(duì)更新,比如你的數(shù)據(jù)變化了,此時(shí)會(huì)生成一個(gè)新的?VNode,然后再拿到保存的_vnode 對(duì)比,就可以得到最小區(qū)域,從而只用更新這部分
所以, _vnode?存放的可以說是當(dāng)前節(jié)點(diǎn),也可以說是舊節(jié)點(diǎn)
另外,_vnode?中保存有一個(gè)?parent,這個(gè)parent?就是外殼節(jié)點(diǎn),上面說?vnode?的時(shí)候已經(jīng)說過了
在哪里賦值?
我們來完整地走一遍流程,涉及源碼很多,但是我已經(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),頁面實(shí)例中是不存在 $vnode?的
本來也想走下流程的,無奈兜兜轉(zhuǎn)轉(zhuǎn)太多,涉及源碼更多
在哪里進(jìn)行賦值?
我就放最后一步?updateChildComponent
updateChildComponent?會(huì)在上個(gè) _vnode?提到的?vm._update?執(zhí)行流程中調(diào)用
function updateChildComponent(parentVnode
? ?vm,
) {
? ?vm.$options._parentVnode = parentVnode;
? ?vm.$vnode = parentVnode;
? ?if (vm._vnode) {
? ? ? ?vm._vnode.parent = parentVnode;
? ?}
}
最后
鑒于本人能力有限,難免會(huì)有疏漏錯(cuò)誤的地方,請(qǐng)大家多多包涵,如果有任何描述不當(dāng)?shù)牡胤?#xff0c;歡迎后臺(tái)聯(lián)系本人,領(lǐng)取紅包
長按關(guān)注>>>
盤它總結(jié)
以上是生活随笔為你收集整理的c++ gdb 绑定源码_【Vue原理】VNode 源码版的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 把qss添加进qrc文件_PDF怎么压缩
- 下一篇: 然子开头的成语有哪些?