日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Vue中template為什么有且只能一個root

這篇文章主要為大家展示了Vue中template為什么有且只能一個root,內(nèi)容簡而易懂,希望大家可以學(xué)習(xí)一下,學(xué)習(xí)完之后肯定會有收獲的,下面讓小編帶大家一起來看看吧。

公司主營業(yè)務(wù):網(wǎng)站設(shè)計制作、成都網(wǎng)站建設(shè)、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出邕寧免費做網(wǎng)站回饋大家。

引言

今年, 疫情并沒有影響到各種面經(jīng)的正常出現(xiàn),可謂是絡(luò)繹不絕(學(xué)不動...)。然后,在前段時間也看到一個這樣的關(guān)于 Vue 的問題, 為什么每個組件 template 中有且只能一個 root?

可能,大家在平常開發(fā)中,用的較多就是 templatehtml 的形式。當(dāng)然,不排除用 JSXrender() 函數(shù)的。但是,究其本質(zhì),它們最終都會轉(zhuǎn)化成 render() 函數(shù)。然后,再由 render() 函數(shù)轉(zhuǎn)為 Vritual DOM (以下統(tǒng)稱 VNode )。而 render() 函數(shù)轉(zhuǎn)為 VNode 的過程,是由 createElement() 函數(shù)完成的。

因此,本次文章將會先講述 Vue 為什么限制 template 有且只能一個 root 。然后,再分析 Vue 如何規(guī)避出現(xiàn)多 root 的情況。那么,接下來我們就從源碼的角度去深究一下這個過程!

一、為什么限制 template 有且只能有一個 root

這里,我們會分兩個方面講解,一方面是 createElement() 的執(zhí)行過程和定義,另一方面是 VNode 的定義。

1.1 createElement()

createElement() 函數(shù)在源碼中,被設(shè)計為 render() 函數(shù)的參數(shù)。所以 官方文檔 也講解了,如何使用 render() 函數(shù)的方式創(chuàng)建組件。

createElement() 會在 _render 階段執(zhí)行:

...
const { render, _parentVnode } = vm.$options
...
vnode = render.call(vm._renderProxy, vm.$createElement);

可以很簡單地看出,源碼中通過 call() 將當(dāng)前實例作為 context 上下文以及 $createElement 作為參數(shù)傳入。

Vue2x 源碼中用了大量的 call 和 apply,例如經(jīng)典的 $set() API 實現(xiàn)數(shù)組變化的響應(yīng)式處理就用的很是精妙,大家有興趣可以看看。

$createElement 的定義又是這樣:

vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)

需要注意的是這個是我們手寫 render() 時調(diào)用的,如果是寫 template 則會調(diào)用另一個 vm._c 方法。兩者的區(qū)別在于 createElement() 最后的參數(shù)前者為 true,后者為 false。

而到這里,這個 createElement() 實質(zhì)是調(diào)用了 _createElement() 方法,它的定義:

export function _createElement (
 context: Component, // vm實例
 tag?: string | Class | Function | Object, // DOM標(biāo)簽
 data?: VNodeData, // vnode數(shù)據(jù)
 children?: any, 
 normalizationType?: number
): VNode | Array {
 ...
}

現(xiàn)在,見到了我們平常使用的 createElement()廬山真面目。這里,我們并不看函數(shù)內(nèi)部的執(zhí)行邏輯,這里分析一下這五個參數(shù):

  • context ,是 Vue_render 階段傳入的當(dāng)前實例
  • tag ,是我們使用 createElement 時定義的根節(jié)點 HTML 標(biāo)簽名
  • data ,是我們使用 createElement 是傳入的該節(jié)點的屬性,例如 class 、 style 、 props 等等
  • children ,是我們使用 createElement 是傳入的該節(jié)點包含的子節(jié)點,通常是一個數(shù)組
  • normalizationType ,是用于判斷拍平子節(jié)點數(shù)組時,要用簡單迭代還是遞歸處理,前者是針對簡單二維,后者是針對多維。

可以看出, createElement() 的設(shè)計,是針對一個節(jié)點,然后帶 children 的組件的 VNode 的創(chuàng)建。并且,它并沒有留給你進(jìn)行多 root 的創(chuàng)建的機會,只能傳一個根 roottag ,其他都是它的選項。

1.2 VNode

我想大家都知道 Vue2x 用的靜態(tài)類型檢測的方式是 flow ,所以它會借助 flow 實現(xiàn)自定義類型。而 VNode 就是其中一種。那么,我們看看 VNode 類型定義:

前面,我們分析了 createElement() 的調(diào)用時機,知道它最終返回的就是 VNode。那么,現(xiàn)在我們來看看 VNode 的定義:

export default class VNode {
 tag: string | void;
 data: VNodeData | void;
 children: ?Array;
 text: string | void;
 elm: Node | void;
 ns: string | void;
 context: Component | void; // rendered in this component's scope
 key: string | number | void;
 componentOptions: VNodeComponentOptions | void;
 componentInstance: Component | void; // component instance
 parent: VNode | void; // component placeholder node

 // strictly internal
 raw: boolean; // contains raw HTML? (server only)
 isStatic: boolean; // hoisted static node
 isRootInsert: boolean; // necessary for enter transition check
 isComment: boolean; // empty comment placeholder?
 isCloned: boolean; // is a cloned node?
 isOnce: boolean; // is a v-once node?
 asyncFactory: Function | void; // async component factory function
 asyncMeta: Object | void;
 isAsyncPlaceholder: boolean;
 ssrContext: Object | void;
 fnContext: Component | void; // real context vm for functional nodes
 fnOptions: ?ComponentOptions; // for SSR caching
 devtoolsMeta: ?Object; // used to store functional render context for devtools
 fnScopeId: ?string; // functional scope id support

 constructor (
 tag?: string,
 data?: VNodeData,
 children?: ?Array,
 text?: string,
 elm?: Node,
 context?: Component,
 componentOptions?: VNodeComponentOptions,
 asyncFactory?: Function
 ) {
 ...
 }
 ...
}

可以看到 VNode 所具備的屬性還是蠻多的,本次我們就只看 VNode 前面三個屬性:

  • tag,即 VNode 對于的標(biāo)簽名
  • data,即 VNode 具備的一些屬性
  • children,即 VNode 的子節(jié)點,它是一個 VNode 數(shù)組

顯而易見的是 VNode 的設(shè)計也是一個 root ,然后由 children 不斷延申下去。這樣和前面 createElement() 的設(shè)計相呼應(yīng), 不可能會出現(xiàn)多 root 的情況。

1.3 小結(jié)

可以看到 VNodecreateElement() 的設(shè)計,就只是針對單個 root 的情況進(jìn)行處理,最終形成 樹的結(jié)構(gòu)。那么,我想這個時候 可能有人會問為什么它們被設(shè)計樹的結(jié)構(gòu)?

而針對這個問題,有 兩個方面,一方面是樹形結(jié)構(gòu)的 VNode 轉(zhuǎn)為真實 DOM 后,我們只需要將根 VNode 的真實 DOM 掛載到頁面中。另一方面是 DOM 本身就是樹形結(jié)構(gòu),所以 VNode 也被設(shè)計為樹形結(jié)構(gòu),而且之后我們分析 template 編譯階段會提到 AST 抽象語法樹,它也是樹形結(jié)構(gòu)。所以,統(tǒng)一的結(jié)構(gòu)可以實現(xiàn)很方便的類型轉(zhuǎn)化,即從 ASTRender 函數(shù),從 Render 函數(shù)到 VNode ,最后從 VNode 到真實 DOM 。

Vue中template為什么有且只能一個root

并且,可以想一個情景,如果多個 root ,那么當(dāng)你將 VNode 轉(zhuǎn)為真實 DOM 時,掛載到頁面中,是不是要遍歷這個 DOM Collection ,然后掛載上去,而這個階段又是操作 DOM 的階段。大家都知道的一個東西就是操作 DOM非常昂貴的。所以,一個 root 的好處在這個時候就體現(xiàn)出它的好處了。

其實這個過程,讓我想起 紅寶書中在講文檔碎片的時候,提倡把要創(chuàng)建的 DOM 先添加到文檔碎片中,然后將文檔碎片添加到頁面中。(PS:想想第一次看紅寶書是去年 4 月份,剛開始學(xué)前端,不經(jīng)意間過了快一年了....)

二、如何規(guī)避出現(xiàn)多 root 的情況

 2.1 template 編譯過程

在我們平常的開發(fā)中,通常是在 .vue 文件中寫