c++ gdb 绑定源码_【Vue原理】VNode 源码版
↑點擊上方?“神仙朱”?一起研究Vue源碼吧
專注?Vue?源碼分享,文章分為白話版和?源碼版,白話版助于理解工作原理,源碼版助于了解內部詳情,讓我們一起學習吧
研究基于 Vue版本【2.5.17】
五一就這么過了,哎,大家快點進入工作狀態哈哈
今天就來探索?VNode?的源碼,VNode?是?Vue2?渲染機制中很重要的一部分,是深入Vue?必須了解的部分
我們以4個問題來開始我們的探索
1、vnode 是什么及其作用
2、vnode 什么時候生成
3、vnode 怎么生成
4、vnode 存放什么信息
5、vnode 存放在哪里
文章很長,看之前值做好準備
VNode是什么及作用
首先,第一個問題已經很爛了,網上有很多相關的內容,為了內容的完整性,所以也放上來哈哈。
VNode?表示?虛擬節點 Virtual DOM,為什么叫虛擬節點呢,因為不是真的?DOM?節點。
他只是用 javascript?對象來描述真實?DOM,這么描述,把DOM標簽,屬性,內容都變成?對象的屬性
就像用?JavaScript?對象描述一個人一樣
{sex:'男', name:'神仙朱', salary:5000,children:null}過程就是,把你的?template?模板?描述成?VNode,然后一系列操作之后通過?VNode?形成真實DOM進行掛載
是什么?
JavaScript?對象
什么用?
1兼容性強,不受執行環境的影響。VNode?因為是?JS?對象,不管?Node?還是?瀏覽器,都可以統一操作, 從而獲得了服務端渲染、原生渲染、手寫渲染函數等能力
2減少操作?DOM。任何頁面的變化,都只使用 VNode?進行操作對比,只需要在最后一步掛載更新DOM,不需要頻繁操作DOM,從而提高頁面性能
VNode怎么生成
在?Vue?源碼中,vnode?是通過一個構造函數生成的,構造函數看起來挺簡單的
本來以為很多內容,帶著沉重的心情去探索,然后看到之后就放松了下來,看了一會,心情再次沉重了起來
其中涉及的內容還是挺多的....不然哪里來開篇的那么多問題
行了,看下?VNode?的構造函數
function VNode(
? ?tag, data, children,
? ?text, elm, context, ? ?componentOptions) { ? ?????this.tag = tag; // 標簽名 ? ?this.data = data; ? ?????this.children = children; // 子元素 ? ?this.text = text; // 文本內容 ? ?this.elm = elm; // Dom 節點 ? ?this.context = context; ? ?????this.componentOptions = componentOptions; ? ?????this.componentInstance = undefined; ? ?????this.parent = undefined; ? ?????this.isStatic = false; // 是否靜態節點 ? ?this.isComment = false; // 是否是注釋節點 ? ?this.isCloned = false; // 是否克隆節點};看完上面,先不要糾結都是什么東西,先來走一遍
比如我們使用?vnode?去描述這樣一個template
<div class="parent" style="height:0" href="2222"> ? ?111111div>使用?VNode?構造函數就可以生成下面的?VNode
{ ? ?
????tag: 'div', ? ?
????data: { ? ? ? ?
????????attrs:{href:"2222"}
? ? ? ?staticClass: "parent", ? ? ? ?
????????staticStyle: { ? ? ? ? ? ?
????????????height: "0"
? ? ? ?} ? ?}, ? ?
????children: [{ ? ? ? ?
????????tag: undefined, ? ? ? ?
????????text: "111111"
? ?}]
}這個?JS?對象,就已經囊括了整個模板的所有信息,完全可以根據這個對象來構造真實DOM了
至于其中都是什么意思,請看下個問題
VNode存放什么信息
新建一個?vnode?的時候,包含了非常多的屬性,每個屬性都是節點的描述的一部分
我們只撿一些屬性來探索一下,了解主體即可
1普通屬性
1data
1、存儲節點的屬性,class,style?等
2、存儲綁定的事件
3、....其他
2elm
真實DOM?節點
生成VNode?的時候,并不存在真實?DOM
elm?會在需要創建DOM 時完成賦值,具體函數在?createElm?中
賦值語句就是一句(簡化了源碼)
3context
渲染這個模板的上下文對象
意思就是,template?里面的動態數據要從這個?context?中獲取,而 context?就是?Vue?實例
如果是頁面,那么context?就是本頁面的實例,如果是組件,context則是組件的實例
4?isStatic
是否是靜態節點
當一個節點被標記為靜態節點的時候,說明這個節點可以不用去更新它了,當數據變化的時候,可以忽略去比對他,以提高比對效率
2組件相關屬性
1parent
這個parent?表示是組件的外殼節點
額,什么是外殼節點,舉個栗子先吧
1、存在這樣一個組件?test
2、頁面中使用這個組件
誒,到這里就有意思了,組件其實應有兩種?VNode
頁面給 組件test? 解析成的?VNode | { ??? tag:"test", ??? children:undefined } |
組件內部生成的?VNode | { ??? tag:"?h2", ? ? children:[{ ??? ???tag:undefined, ??? ???text:"我勒個去" ??? }] } |
這兩種VNode?名義上都是對的,都有理,誰是正牌不好說
最后尤大判定第一個?VNode?是?第二個?VNode?的爸爸,也就是外殼節點
外殼節點通常是?父組件和?子組件的?關聯,用于保存一些父組件傳給子組件的數據?等
2 componentInstance
這個顧名思義,就是組件生成的實例,保存在這里
上面?test?組件的外殼節點中的?componentInstance
3 componentOptions
這個就存儲一些?父子組件?PY?交易的證據
比如?props,事件,slot 什么的,打印看下
其中?children?保存的就是?slot,listeners?保存?事件,propsData?保存?props
VNode怎么生成
在初始化完選項,解析完模板之后,就需要掛載?DOM了。此時就需要生成?VNode,才能根據?VNode?生成?DOM?然后掛載
掛載?DOM?第一步,就是先執行渲染函數,得到整個模板的?VNode
比如有以下渲染函數,執行會返回?VNode,就是 _c?返回的VNode
function (){
? ?with(this){ ?
? ? ? ?return _c('div',{attrs:{"href":"xxxx"}},["1111"]).
? ?}
}渲染函數會綁定上下文對象,加上?with?的作用,_c?其實就是?vm._c
現在就來看 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標簽) {
? ? ? ?vnode = new VNode(
????????????tag, data, children, undefined,
????????????undefined, context
????????);
? ?}
? ?else if (tag 是組件) {
? ? ? ?vnode = createComponent(
????????????Ctor, data, context,
????????????children, tag
????????);
? ?} ? ?
????return vnode
}
我們可以看到,正常標簽 和?組件會走不同流程
1正常標簽
比如有這樣一個正常標簽模板
解析成渲染函數如下
function (){ ? ?
????with(this){ ?
? ? ? ?return _c('div',{
????????????attrs:{"href":"xxxx"}},
????????????["1111"]
????????)
? ?}
}看上面_c?源碼,可以知道經過 _c?把參數傳導,這樣去構建?VNode
new VNode(tag, data, children, undefined, undefined, context);| tag | ?'div' |
| data | {attrs:{"href":"xxxx"}} |
| children | ?["1111"] |
| context | ?頁面實例 |
這樣就保存了?tag,data,children?和?context
2組件
比如頁面使用了test組件
解析成渲染函數如下
with(this){ ?
? ?return _c('div',[
? ? ? ?_c('test',
? ? ? ? ? ?{attrs:{"name":name}},
? ? ? ? ? ?["1111"]
? ? ? ?)
? ?],1)
}看上面 _c?代碼知道 ,_c 會先調用?createComponent
createComponent(Ctor, data, context, children, tag);}| tag | 'test' |
| data | { attrs:{"name":name} } |
| children | ?["1111"] |
| context | ?頁面父實例(畢竟這是外殼節點,是在父實例中解析的) |
| Ctor | 組件的選項,然后變成組件的構造函數,這里可以先不管,后面會詳細講 Component? |
createComponent 中也會調用?VNode?構造函數,生成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存放在哪里
那么創建出來的?VNode?是否有被存起來,毫無疑問,肯定是要的啊
主要是三個位置存了?vnode,分別是
parent ,_vnode ,$vnode?
parent?上面已經說過,就先不提了,剩下兩個全部是掛在?Vue?實例一級屬性上的
打印一下組件的實例,可以很清楚看到這兩個屬性
下面來說說這兩個東西
1_vnode
_vnode?存放表示當前節點的?VNode
什么叫當前,也就是可以通過這個VNode?直接映射成?當前真實DOM
他的作用是什么呢?
用來比對更新,比如你的數據變化了,此時會生成一個新的?VNode,然后再拿到保存的_vnode 對比,就可以得到最小區域,從而只用更新這部分
所以, _vnode?存放的可以說是當前節點,也可以說是舊節點
另外,_vnode?中保存有一個?parent,這個parent?就是外殼節點,上面說?vnode?的時候已經說過了
在哪里賦值?
我們來完整地走一遍流程,涉及源碼很多,但是我已經非常精簡了,大概了解個流程
function Vue() {
? ?...初始化組件選項等
? ?mountComponent()
}
function mountComponent() {
? ?....解析模板,生成渲染函數
?
? ?// 用于生成VNode,生成DOM,掛載DOM
? ?updateComponent = function() {
? ? ? ?vm._update(vm._render()); ? ?}; ? ?
? ?// 新建 watcher,保存updateComponent為更新函數,新建的時候會立即執行一遍
? ?new Watcher(vm, updateComponent)}
function Watcher(vm, expOrFn) { ? ?
????this.getter = expOrFn ; ? ?
????this.getter()
}
// 執行前面解析得到的渲染函數,返回生成的 VNode
Vue.prototype._render = () {}
// 根據vnode,生成DOM 掛載
Vue.prototype._update = function(vnode) { ? ?
????var prevVnode = vm._vnode;
? ?vm._vnode = vnode; ? ?
????if (不存在舊節點) { ...使用vnode創建DOM并直接掛載 } ? ?
????else { ...存在舊節點,開始比對舊節點和新節點,然后創建DOM并掛載 }
}
2$vnode
$vnode?只有組件實例才有,因為 $vnode?存放的是外殼節點,頁面實例中是不存在 $vnode?的
本來也想走下流程的,無奈兜兜轉轉太多,涉及源碼更多
在哪里進行賦值?
我就放最后一步?updateChildComponent
updateChildComponent?會在上個 _vnode?提到的?vm._update?執行流程中調用
function updateChildComponent(parentVnode
? ?vm,
) {
? ?vm.$options._parentVnode = parentVnode;
? ?vm.$vnode = parentVnode;
? ?if (vm._vnode) {
? ? ? ?vm._vnode.parent = parentVnode;
? ?}
}
最后
鑒于本人能力有限,難免會有疏漏錯誤的地方,請大家多多包涵,如果有任何描述不當的地方,歡迎后臺聯系本人,領取紅包
長按關注>>>
盤它總結
以上是生活随笔為你收集整理的c++ gdb 绑定源码_【Vue原理】VNode 源码版的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 把qss添加进qrc文件_PDF怎么压缩
- 下一篇: greenplum 数据库如何增加列_G