【问题标题】:A way to render multiple root elements on VueJS with v-for directive一种使用 v-for 指令在 VueJS 上渲染多个根元素的方法
【发布时间】:2018-05-10 17:39:05
【问题描述】:

现在,我正在尝试创建一个显示最近新闻帖子的网站,该网站由我的 NodeJS API 提供。

我尝试了以下方法:

HTML

<div id="news" class="media" v-for="item in posts">
    <div>
        <h4 class="media-heading">{{item.title}}</h4>
        <p>{{item.msg}}</p>
    </div>
</div>

JavaScript

const news = new Vue({
    el: '#news',
    data:   {
        posts:  [
            {title: 'My First News post',   msg: 'This is your fist news!'},
            {title: 'Cakes are great food', msg: 'Yummy Yummy Yummy'},
            {title: 'How to learnVueJS',    msg: 'Start Learning!'},
        ]
    }
})

显然,上述方法不起作用,因为 Vue 无法渲染多个根元素。

我查阅了 VueJS 的官方手册并没有找到解决方案。 谷歌搜索了一段时间后,我明白不可能渲染多个根元素,但是,我还没有想出一个解决方案。

【问题讨论】:

    标签: components vuejs2


    【解决方案1】:

    我发现添加多个根元素的最简单方法是添加单个 &lt;div&gt; 包装元素,并使用一些 CSS 魔法使其消失以进行渲染。

    为此,我们可以使用“display: contents” CSS 属性。效果是它使容器消失,使元素的子元素在 DOM 中更上一层楼。

    因此,在你的 Vue 组件模板中你可以有这样的东西:

    <template>
        <div style="display: contents"> <!-- my wrapper div is rendered invisible -->
            <tr>...</tr>
            <tr>...</tr>
            <tr>...</tr>
        </div>
    </template>
    

    我现在可以使用我的组件,而浏览器不会弄乱格式,因为包装 &lt;div&gt; 根元素将被浏览器忽略以进行显示:

    <table>
       <my-component></my-component> <!-- the wrapping div will be ignored -->
    </table>
    

    但是请注意,尽管这应该在大多数浏览器中都可以使用,但您可能需要check here 以确保它可以处理您的目标浏览器。

    【讨论】:

      【解决方案2】:

      定义一个custom directive

      Vue.directive('fragments', {
        inserted: function(el) {
          const children = Array.from(el.children)
          const parent = el.parentElement
          children.forEach((item) => { parent.appendChild(item) })
          parent.removeChild(el)
        }
      });
      

      然后你可以在组件的根元素中使用它

      <div v-fragments>
        <tr v-for="post in posts">...</tr>
      </div>
      

      根元素不会在DOM中渲染,在渲染表格时尤其有效。

      【讨论】:

      • 这似乎根本无法处理反应性。 update 指令事件似​​乎也不会在添加/删除/移动子项时触发,因此无法将其用于反应性示例。
      【解决方案3】:

      您可以使用render functions 拥有多个根元素(或组件)

      一个简单的例子是有一个渲染多个&lt;li&gt;元素的组件:

      <template>
       <li>Item</li>
       <li>Item2</li>
       ... etc
      </template>
      

      但是上面会抛出一个错误。为了解决这个错误,上面的模板可以转换为:

      export default {
       functional: true,
       render(createElement) {
        return [
          createElement('li', 'Item'),
          createElement('li', 'Item2'),
        ]
       }
      }
      

      但正如您可能注意到的那样,如果您想显示 50 li 的项目,这可能会变得非常乏味。因此,最终,您可以动态显示元素:

      export default {
       functional: true,
       props: ['listItems'], //this is an array of `<li>` names (e.g. ['Item', 'Item2'])
      
       render(createElement, { props }) {
         return props.listItems.map(name => {
           return createElement('li', name)
         })
       }
      }
      

      INFO 在这些示例中,我使用了属性 functional: true 但当然不需要使用“渲染函数”。请考虑进一步了解功能组件here

      【讨论】:

      • 老问题,但我只想补充一点,这条规则在 Vue 3 中将消失。在 Vue 3 中,您可以在普通的旧组件中拥有多个根元素。
      • 在 Vue 2 中这不起作用。即使使用自定义渲染功能,我也会得到:[Vue warn]: Multiple root nodes returned from render function. Render function should return a single root node.
      【解决方案4】:

      Vue 要求有一个根节点。但是,请尝试将您的 html 更改为:

      <div id="news" >
          <div class="media" v-for="item in posts">
             <h4 class="media-heading">{{item.title}}</h4>
             <p>{{item.msg}}</p>
          </div>
      </div>
      

      此更改允许单个根节点 id="news",但仍允许呈现最近发布的列表。

      【讨论】:

      • 非常感谢您抽出宝贵时间回答我的问题,罗恩!成功了!!!
      • 优秀。请点击答案旁边的复选标记接受答案。谢谢,很高兴能帮上忙。
      【解决方案5】:

      在 Vue 3 中,this is supported 你正在尝试:

      在 3.x 中,组件现在可以有多个根节点!但是,这确实需要开发人员明确定义属性应该分布在哪里。

      <!-- Layout.vue -->
      <template>
        <header>...</header>
        <main v-bind="$attrs">...</main>
        <footer>...</footer>
      </template>
      

      【讨论】:

        【解决方案6】:

        Vue 不支持多个根元素(这是由您的 v-for 指令引起的,因为它可能会渲染超过 1 个元素)。而且解决起来也很简单,只要把你的 HTML 包装到另一个 Element 就可以了。

        例如:

        <div id="app">
           <!-- your HTML code -->
        </div>
        

        和js:

        var app = new Vue({
          el: '#app', // it must be a single root!
          // ...
        })
        

        【讨论】:

        • 不幸的是,当您必须有两个根元素时,这不是一个合理的解决方案。例如组件库需要这样的配置的情况。一个问题甚至 AngularJS 在很久以前就成功地解决了他们的 ng-repeat-startng-repeat-end
        • 想象一下,如果你的根元素也是 tr,那么 html 指定不允许将 div(或除 thead、tbody、tr、tfoot 之外的任何其他标签)放入 table 标签内
        猜你喜欢
        • 2019-12-19
        • 2018-06-09
        • 2019-05-09
        • 1970-01-01
        • 1970-01-01
        • 2020-03-13
        • 2020-12-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多