Vue中是否有类似于React.Fragment的东西?

36
在React中我可以这样做:
// parent component
export default (){
  return (
     <div>
       <div>1</div>
       <ChildComponent />
       <div>5</div>
     </div>
  );
}

// child component
export default (){
  return (
     <React.Fragment>
       <div>2</div>
       <div>3</div>
       <div>4</div>
     </React.Fragment>
  );
};

// compiled html tags in browser .
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>

但是在Vue中,当我做同样的事情时,出现了一些不同之处。

// parent component
<template>
    <div>
        <div>1</div>
        <child-component />
        <div>5</div>
    </div>
</template>

<script>
    import childComponent from 'path/to/childComponent';
    export default {
      components: { childComponent }
    }
</script>
-------------------------------------------------------------
// child component
<template>
    <div id='have_to_write_in_vue'>
        <div>2</div>
        <div>3</div>
        <div>4</div>
    <div>
</template>

// compiled html tags in browser .
<div>1</div>
<div id='have_to_write_in_vue'>
    <div>2</div>
    <div>3</div>
    <div>4</div>
</div>
<div>5</div>

标签在Vue中的层级不同,这是它与'DIV'标签的区别。 我该怎么处理?
我问这个问题是因为当出现无用的嵌套时会出现问题。

2
有类似的东西,请查看这个Github。这可能只是其中一个模块。 - Mike Donkers
8个回答

25

15

在Vue中缺少第一方的Fragment组件让我感到很头疼!

我查看了vue-fragment npm包,但是它让我感到有些紧张,因为他们所做的太多了,我觉得肯定有更好的方法。

我想出了一些非常轻量级的东西......它能工作是因为函数式组件允许返回多个根节点。

然而,这不像React那样可以100%工作。在React中,您实际上可以将React.Fragment用作应用程序的根节点,例如。在Vue中只有当您已经在组件内部时才会生效(也就是说,它不能成为基础组件的根节点)。

const Fragment = {
  functional: true,
  render: (h, ctx) => ctx.children
}

使用它的方法是在需要的地方添加以下代码...

<Fragment>
  <div>Div 1</div>
  <div>Div 2</div>
</Fragment>

非常有用的...

{someCondition && (
  <Fragment>
    <div>yay i can use multiple nodes</div>
    <div>:)</div>
  </Fragment>
)}

<div>
  Some inline text {condition ? (
    <Fragment>now I can use <strong>html</strong> and {variables}</Fragment>
  ) : (
    <Fragment>So many possibilities</Fragment>
  )}
</div>

2
这是一个使用ctx.children的功能组件。Vue函数式组件允许多个根节点。函数式组件的代码在答案中已经给出,如下:const Fragment = {...} - Roi
1
好的,谢谢。但是不幸的是,我在Vue v2.6.11上遇到了一个错误,它说:“[Vue warn]: Multiple root nodes returned from render function. Render function should return a single root node.” - André Kelling
你好!即使我也说 [Vue warn]: 渲染函数返回了多个根节点。渲染函数应该只返回一个根节点。 使用 "vue": "2.6.10" - Sofienne Lassoued
这真是奇怪!我一两周前尝试过这种方法,但它不起作用。然后,今天我再次尝试了一下,它就像魔法一样工作了!我希望我能记得那时候尝试做了什么,但无论如何,我可以肯定地说它确实可行。话虽如此,如果我再次遇到这个 bug,我会尝试记住这篇帖子并向人们更新可能的问题原因。 - isaacsan 123
问题在于你使用它的位置和方式... 它不能作为组件的根节点使用,只能作为JSX中的子节点。 - Roi
显示剩余5条评论

8

2
Vue一直支持嵌套<template>,并且它们的工作方式与<React.fragment>相同。这不是在v3中添加的内容。v2和v3之间唯一的区别在于,在v2中,组件的根<template>必须仅限于一个子元素。现在这个限制已经消失了,代价是不再有$el。现在,如果您想在模板中使用任何DOM元素,您必须使用$ref - tao
1
没错,感谢更新。这里我指的是多根片段,在v3中是新功能。在React中,您可以使用<React.Fragment>(或<></>)来克服单根限制,这也是OP所要求的,我想。 - dakujem

7

Vue v3

这是对 @Roi 的 Vue v3 简单的适应。

如@dakujem所提到的,Vue v3 支持原生多根组件。然而它不支持深度嵌套的片段。

更新:你可以嵌套<template>


<template>
  <div>
    <ul>
      <li>A</li>
      <li>B</li>
      <template>
        <li>C</li>
        <li>D</li>
      </template>
    </ul>
  </div>
</template>


当然可以。您可以嵌套SLOTs(v2和v3都支持)。 - dakujem
1
<template> doesn't work "standalone". It needs a v-if or v-for. And <template v-if="true"> works as well. - Polv

2

TLDR:创建一个组件Frag.vue

<template>
    <slot></slot>
</template>

如其他答案所述,您可以嵌套template,但有一个注意点:

您可以使用v-if

<template>
  <ul>
    <li>
      first
    </li>
    <template v-if="true">
      <li>
        second
      </li>
      <li>
        third
      </li>
    </template>
    <li>
      fourth
    </li>
  </ul>
</template>

生成:

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
  <li>fourth</li>
</ul>

或者v-for

<template>
  <ul>
    <li>
      first
    </li>
    <template v-for="i in list">
      <li>
        second {{i}}
      </li>
      <li>
        third {{i}}
      </li>
    </template>
    <li>
      fourth
    </li>
  </ul>
</template>

生成:

<ul>
  <li>first</li>
  <li>second 0</li>
  <li>third 0</li>
  <li>second 1</li>
  <li>third 1</li>
  <li>fourth</li>
</ul>

但是,没有 v-for 或者 v-if

<template>
  <ul>
    <li>
      first
    </li>
    <template>
      <li>
        second
      </li>
      <li>
        third
      </li>
    </template>
    <li>
      fourth
    </li>
  </ul>
</template>

它产生

<ul>
  <li>first</li>
  <li>fourth</li>
</ul>

你可以通过始终使用v-if="true"来解决这个问题,但这并不是最理想的方法。

话虽如此,由于模板中的插槽将所有传递的子元素都分散到默认插槽中,因此您可以创建一个非常简单的组件:

<template>
    <slot></slot>
</template>

就是这样了。取名为 Frag.vue,在需要的位置导入即可。你可以使用它和 v-ifv-for 或者两者都不用一起使用。

<template>
  <ul>
    <li>
      first
    </li>
    <Frag v-if="true">
      <li>
        second (v-if)
      </li>
      <li>
        third (v-if)
      </li>
    </Frag>
    <Frag v-for="i in list">
      <li>
        second {{i}} (v-for)
      </li>
      <li>
        third {{i}} (v-for)
      </li>
    </Frag>
    <Frag>
      <li>
        second (nothing)
      </li>
      <li>
        third (nothing)
      </li>
    </Frag>
    <li>
      fourth
    </li>
  </ul>
</template>

生成:

<ul>
  <li>
    first
  </li>
  <li>
    second (v-if)
  </li>
  <li>
    third (v-if)
  </li>
  <li>
    second 0 (v-for)
  </li>
  <li>
    third 0 (v-for)
  </li>
  <li>
    second 1 (v-for)
  </li>
  <li>
    third 1 (v-for)
  </li>
  <li>
    second (nothing)
  </li>
  <li>
    third (nothing)
  </li>
  <li>
    fourth
  </li>
</ul>

1

对于使用Vue 3 + JSX/TSX的人,你可以选择以下任一方法:

<>
  <p>one</p>
  <p>two</p>
</>

或者
import { Fragment } from 'vue';

<Fragment>
  <p>one</p>
  <p>two</p>
</Fragment>

注意:我使用@vue/babel-plugin-jsx来支持JSX/TSX。

1
Module '"../../node_modules/vue/dist/vue"' has no exported member 'Fragment' - Davo

0
发现了这个很棒的指令,经过实战检验!https://github.com/privatenumber/vue-frag
<template>
    <div v-frag> <!-- This element will be unwrapped -->

        <div v-for="i in 10">
            {{ i }}
        </div>
    </div>
</template>

<script>
import frag from 'vue-frag';

export default {
    directives: {
        frag
    }
};
</script>

从 README 中:

这与 vue-fragment 有何不同?

它们都旨在完成相同的任务。但是,vue-fragment 是一个组件,而 vue-frag 是一个指令。当我发现 vue-fragment 没有任何测试以确保正确的行为、有很多未处理的问题并且似乎没有得到积极维护时,我制作了 vue-frag。就大小而言,它们都很小,但 vue-frag 稍微小一些(993B 对比 798B)。


-1

只需使用嵌套的template标签来包装多个子Vue组件。

<template>
  <template>
    <div class="my-first-child"></div>
    <div class="my-second-child"></div>
    <div class="my-third-child"></div>
  </template>
</template>

这将导致

<div class="my-first-child"></div>
<div class="my-second-child"></div>
<div class="my-third-child"></div>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接