天天看點

一個 Vue 模闆可以有多個根節點(Fragments)?

作者: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...