一個 Vue 模板可以有多個根節(jié)點(Fragments)?
如果我們試圖創(chuàng)建一個沒有根節(jié)點的Vue模板,比如這樣:
- <template>
- <div>Node 1</div>
- <div>Node 2</div>
- </template>
我們就會收到編譯或運行時錯誤,因為模板必須具有單個根元素。
通常,我們通過在最外層包裹一層 div 來解決這個問題,但這個div元素一般沒有啥使用,就是讓模板符合單根需求。
- <template>
- <div><!--只是來包裝一下-->
- <div>Node 1</div>
- <div>Node 2</div>
- </div>
- </template>
這樣的方式通常問題不在,但是在某些情況下,擁有多根模板是必要的。在本文中,我們來探討一下何時需要以及如何解決多根的問題。
渲染數(shù)組
某些情況下,可能需要組件渲染子節(jié)點數(shù)組以包含在父組件中。
例如,一些CSS特性需要非常特殊的元素層次結(jié)構(gòu)才能正確工作,比如CSS grid或flex,不能在父元素和子元素之間使用包裝器。
還有一個問題,在組件中添加包裝元素可能會導(dǎo)致渲染無效的HTML。例如,如果要構(gòu)建table,則表行必須僅具有用于子項的表單元格。
簡而言之,單根需求意味著在Vue中將無法返回子元素的組件的設(shè)計模式。
Fragments
這個單根限制對于React也是一個問題,但是它在版本16中提供了一個稱為[fragments][1]的功能。要使用它,只需要將多根模板包裝在特殊的React.Fragment元素中:
這將使子組件沒有多余包裝,還有一個簡潔的短語法<>:
Vue中的 Fragments
那么 Vue 是否也會引入 fragments?這可能不會很快,原因是虛擬DOM差異算法依賴于具有單個根的組件。根據(jù)Vue貢獻者Linus Borg的說法:
“允許 fragments 需要對[diffing]算法進行重大更改…不僅要使其能夠正常工作,而且還必須使其具有高性能。…這是一項非常繁重的任務(wù)” |
具有渲染功能的函數(shù)組件
函數(shù)組件沒有單根限制,因為它們不需要像有狀態(tài)組件那樣在虛擬DOM中進行區(qū)分。這意味著,如果組件只需要返回靜態(tài)HTML,那么擁有多個根節(jié)點也沒什么問題。
還有一個警告:我們需要使用渲染功能,因為vue-loader當(dāng)前不支持多根功能([盡管對此進行了討論][2])。
使用指令技巧
還可以使用一種簡單的方法來繞過單根限制。就是使用自定義指令,首先我們先所包裹的元素刪除
之前的:
- <parent>
- <wrapper>
- <child/>
- <child/>
- </wrapper>
- </parent>
中間步驟:
- <parent>
- <wrapper/>
- <child/>
- <child/>
- </parent>
最終:
- <parent>
- <!-- 刪除 <wrapper/> -->
- <child/>
- <child/>
- </parent>
要使它正常工作有點棘手,這里可以使用由Julien Barbay寫的 [vue-fragments][3] 的插件。
vue-fragments
vue-fragments可以作為一個插件安裝到你的Vue項目中
- import { Plugin } from "vue-fragments";
- Vue.use(Plugin);
該插件注冊了一個全局VFragment組件,將其用作組件模板中的包裝器,類似于React片段的語法:
- <template>
- <v-fragment>
- <div>Fragment 1</div>
- <div>Fragment 2</div>
- </v-fragment>
- </template>
我不確定這個插件在所有的用例中有多健壯——它看起來可能是脆弱的——但在我做的實驗中,它工作得很好。