前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >一个 Vue 模板可以有多个根节点(Fragments)?

一个 Vue 模板可以有多个根节点(Fragments)?

作者头像
前端小智@大迁世界
发布2020-05-12 11:53:14
3K0
发布2020-05-12 11:53:14
举报
文章被收录于专栏:终身学习者终身学习者

作者:Anthony Gore 译者:前端小智 来源:vuejsdevelopers

如果我们试图创建一个没有根节点的Vue模板,比如这样:

代码语言:javascript
复制
<template>
  <div>Node 1</div>
  <div>Node 2</div>
</template>

我们就会收到编译或运行时错误,因为模板必须具有单个根元素。

通常,我们通过在最外层包裹一层 div 来解决这个问题,但这个div元素一般没有啥使用,就是让模板符合单根需求。

代码语言:javascript
复制
<template>
  <div><!--只是来包装一下-->
    <div>Node 1</div>
    <div>Node 2</div>
  </div>
</template>

这样的方式通常问题不在,但是在某些情况下,拥有多根模板是必要的。在本文中,我们来探讨一下何时需要以及如何解决多根的问题。

渲染数组

某些情况下,可能需要组件渲染子节点数组以包含在父组件中。

例如,一些CSS特性需要非常特殊的元素层次结构才能正确工作,比如CSS gridflex,不能在父元素和子元素之间使用包装器。

代码语言:javascript
复制
<template>
  <div style="display:flex">
    <!-- 如果子组件有多包裹一层那么 flex 不能正常工作-->
    <FlexChildren/>
  </div>
</template>

还有一个问题,在组件中添加包装元素可能会导致渲染无效的HTML。 例如,如果要构建table,则表行<tr>必须仅具有用于子项的表单元格<td>

代码语言:javascript
复制
<template>
  <table>
    <tr>
      <!--使用div包装器会使这个HTML无效-->
      <TableCells/>
    </tr>
  </table>
</template>

简而言之,单根需求意味着在Vue中将无法返回子元素的组件的设计模式。

Fragments

这个单根限制对于React也是一个问题,但是它在版本16中提供了一个称为fragments的功能。 要使用它,只需要将多根模板包装在特殊的React.Fragment元素中:

代码语言:javascript
复制
class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

这将使子组件没有多余包装,还有一个简洁的短语法<>

代码语言:javascript
复制
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当前不支持多根功能(尽管对此进行了讨论)。

代码语言:javascript
复制
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
  }
});

使用指令技巧

还可以使用一种简单的方法来绕过单根限制。就是使用自定义指令,首先我们先所包裹的元素删除

之前的:

代码语言:javascript
复制
<parent>
  <wrapper>
    <child/>
    <child/>
  </wrapper>
</parent>

中间步骤:

代码语言:javascript
复制
<parent>
  <wrapper/>
  <child/>
  <child/>
</parent>

最终:

代码语言:javascript
复制
<parent>
  <!-- 删除 <wrapper/> -->
  <child/>
  <child/>
</parent>

要使它正常工作有点棘手,这里可以使用由Julien Barbay写的 vue-fragments 的插件。

vue-fragments

vue-fragments可以作为一个插件安装到你的Vue项目中

代码语言:javascript
复制
import { Plugin } from "vue-fragments";
Vue.use(Plugin);

该插件注册了一个全局VFragment组件,将其用作组件模板中的包装器,类似于React片段的语法:

代码语言:javascript
复制
<template>
  <v-fragment>
    <div>Fragment 1</div>
    <div>Fragment 2</div>
  </v-fragment>
</template>

我不确定这个插件在所有的用例中有多健壮——它看起来可能是脆弱的——但在我做的实验中,它工作得很好。


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

https://vuejsdevelopers.com/2...

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 渲染数组
  • Fragments
  • Vue中的 Fragments
  • 具有渲染功能的函数组件
  • 使用指令技巧
  • vue-fragments
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档