作者:Anthony Gore
譯者:前端小智
來源:vuejsdevelopers
如果我們試圖建立一個沒有根節點的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>
複制
這樣的方式通常問題不在,但是在某些情況下,擁有多根模闆是必要的。在本文中,我們來探讨一下何時需要以及如何解決多根的問題。
渲染數組
某些情況下,可能需要元件渲染子節點數組以包含在父元件中。
例如,一些CSS特性需要非常特殊的元素層次結構才能正确工作,比如
CSS grid
或
flex
,不能在父元素和子元素之間使用包裝器。
<template>
<div style="display:flex">
<!-- 如果子元件有多包裹一層那麼 flex 不能正常工作-->
<FlexChildren/>
</div>
</template>
複制
還有一個問題,在元件中添加包裝元素可能會導緻渲染無效的HTML。 例如,如果要建構table,則表行
<tr>必
須僅具有用于子項的表單元格
<td>
。
<template>
<table>
<tr>
<!--使用div包裝器會使這個HTML無效-->
<TableCells/>
</tr>
</table>
</template>
複制
簡而言之,單根需求意味着在Vue中将無法傳回子元素的元件的設計模式。
Fragments
這個單根限制對于React也是一個問題,但是它在版本16中提供了一個稱為fragments的功能。 要使用它,隻需要将多根模闆包裝在特殊的
React.Fragment
元素中:
class Columns extends React.Component {
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}
}
複制
這将使子元件沒有多餘包裝,還有一個簡潔的短文法
<>
:
class Columns extends React.Component {
render() {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
}
}
複制
Vue中的 Fragments
那麼 Vue 是否也會引入
fragments
?這可能不會很快,原因是虛拟
DOM
差異算法依賴于具有單個根的元件。 根據Vue貢獻者Linus Borg的說法:
“允許 fragments 需要對[diffing]算法進行重大更改...不僅要使其能夠正常工作,而且還必須使其具有高性能。...這是一項非常繁重的任務”
具有渲染功能的函數元件
函數元件沒有單根限制,因為它們不需要像有狀态元件那樣在虛拟DOM中進行區分。這意味着,如果元件隻需要傳回靜态HTML,那麼擁有多個根節點也沒什麼問題。
還有一個警告:我們需要使用渲染功能,因為
vue-loader
目前不支援多根功能(盡管對此進行了讨論)。
export default {
functional: true,
render: h => [
h('tr', [
h('td', 'foo'),
h('td', 'bar'),
]),
h('tr', [
h('td', 'lorem'),
h('td', 'ipsum'),
])
];
});
-------------------------------------------
import TableRows from "TableRows";
new Vue({
el: '#app',
template: `<div id="app">
<table>
<table-rows></table-rows>
</table>
</div>`,
components: {
TableRows
}
});
複制
使用指令技巧
還可以使用一種簡單的方法來繞過單根限制。就是使用自定義指令,首先我們先所包裹的元素删除
之前的:
<parent>
<wrapper>
<child/>
<child/>
</wrapper>
</parent>
複制
中間步驟:
<parent>
<wrapper/>
<child/>
<child/>
</parent>
複制
最終:
<parent>
<!-- 删除 <wrapper/> -->
<child/>
<child/>
</parent>
複制
要使它正常工作有點棘手,這裡可以使用由
Julien Barbay
寫的 vue-fragments 的插件。
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>
複制
我不确定這個插件在所有的用例中有多健壯——它看起來可能是脆弱的——但在我做的實驗中,它工作得很好。
代碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。
https://vuejsdevelopers.com/2...