【问题标题】:Vue js 错误:组件模板应该只包含一个根元素
【发布时间】:2017-12-13 12:10:09
【问题描述】:

我不知道错误是什么,目前我正在通过控制台日志进行测试,以检查选择文件(用于上传)后的更改。

当我运行$ npm run watch 时,我收到以下错误:

“Webpack 正在查看文件……

95% 排放

ERROR 编译失败,出现 1 个错误
19:42:29

./resources/assets/js/components/File.vue 中的错误

(发出的值而不是错误的实例)Vue 模板语法 错误:

组件模板应该只包含一个根元素。如果你 在多个元素上使用 v-if,使用 v-else-if 链接它们 而是。

@ ./resources/assets/js/components/AvatarUpload.vue 5:2-181 @ ./resources/assets/js/app.js @多./resources/assets/js/app.js ./resources/assets/sass/app.scss"

我的 File.vue 是

<template>
        <div class="form-group">
            <label for="avatar" class="control-label">Avatar</label>
            <input type="file" v-on:change="fileChange" id="avatar">
            <div class="help-block">
                Help block here updated 4 ???? ...
            </div>
        </div>

        <div class="col-md-6">
            <input type="hidden" name="avatar_id">
            <img class="avatar" title="Current avatar">
        </div>
</template>

<script>
    export default{
        methods: {
            fileChange(){
                console.log('Test of file input change')
            }
        }
    }
</script>

关于如何解决这个问题的任何想法?究竟是什么错误?

【问题讨论】:

  • 如果您的 拼写错误,您也会收到此错误。发生在我的一个朋友身上...

标签: javascript node.js laravel vue.js


【解决方案1】:

注意此答案仅适用于 Vue 2.x 版本。 Version 3 has lifted this restriction.

您的模板中有两个根元素。

<div class="form-group">
  ...
</div>
<div class="col-md-6">
  ...
</div>

你需要一个。

<div>
    <div class="form-group">
      ...
    </div>

    <div class="col-md-6">
      ...
    </div>
</div>

基本上是in Vue you must have only one root element in your templates

【讨论】:

  • 这个限制在 Vue 3 fwiw 中消失了。不幸的是,我现在找不到这方面的来源,但他们在 VueConf 2020 上宣布了它。
  • 我在 VS Code 中编辑了 Vue 3 项目,而 Vetur 扩展正在为此哭泣 :( 总的来说,我是 Vue 的新手,尤其是 Vue 3,这个扩展比帮助​​更令人困惑
【解决方案2】:

您需要将所有 html 包装到一个元素中。

<template>
   <div>
        <div class="form-group">
            <label for="avatar" class="control-label">Avatar</label>
            <input type="file" v-on:change="fileChange" id="avatar">
            <div class="help-block">
                Help block here updated 4 ? ...
            </div>
        </div>

        <div class="col-md-6">
            <input type="hidden" name="avatar_id">
            <img class="avatar" title="Current avatar">
        </div>
   </div>

</template>

<script>
    export default{
        methods: {
            fileChange(){
                console.log('Test of file input change')
            }
        }
    }
</script>

【讨论】:

    【解决方案3】:

    如果出于任何原因,您不想添加包装器(在我的第一种情况下,它是用于 &lt;tr/&gt; 组件),您可以使用函数式组件。

    components/MyCompo.vue 文件夹中的文件很少:

    • components/MyCompo/index.js
    • components/MyCompo/File.vue
    • components/MyCompo/Avatar.vue

    使用这种结构,您调用组件的方式不会改变。

    components/MyCompo/index.js文件内容:

    import File from './File';
    import Avatar from './Avatar';   
    
    const commonSort=(a,b)=>b-a;
    
    export default {
      functional: true,
      name: 'MyCompo',
      props: [ 'someProp', 'plopProp' ],
      render(createElement, context) {
        return [
            createElement( File, { props: Object.assign({light: true, sort: commonSort},context.props) } ),
            createElement( Avatar, { props: Object.assign({light: false, sort: commonSort},context.props) } )
        ]; 
      }
    };
    

    如果您在两个模板中都使用了一些函数或数据,请将它们作为属性传递,就是这样!

    我让您想象使用这种模式构建组件列表和许多功能。

    【讨论】:

    • 您好,有没有其他方法可以阻止根 div 在模板渲染后显示?
    • 很快,答案是:不。但是你总是可以通过 DOM api 做任何你想做的事情。您不应该使用模板生成的 DOM,它会导致与虚拟 DOM(或充当的东西)不同步。由于更改了模板生成的 dom,您可能会再次生成模板,因此您的模板会生成两次。
    【解决方案4】:

    更完整的答案:http://www.compulsivecoders.com/tech/vuejs-component-template-should-contain-exactly-one-root-element/

    但基本上:

    • 目前,一个 VueJS 模板只能包含一个根元素(由于渲染问题)
    • 如果您确实需要两个根元素,因为 HTML 结构不允许您创建包装父元素,您可以使用 vue-fragment

    安装它:

    npm install vue-fragment
    

    使用它:

    import Fragment from 'vue-fragment';
    Vue.use(Fragment.Plugin);
    
    // or
    
    import { Plugin } from 'vue-fragment';
    Vue.use(Plugin);
    

    然后,在您的组件中:

    <template>
      <fragment>
        <tr class="hola">
          ...
        </tr>
        <tr class="hello">
          ...
        </tr>
      </fragment>
    </template>
    

    【讨论】:

    • 恕我直言,我有 2 个反对 v-fragment 的论点:1)这是一个不必要的依赖,因为你通过内置的功能组件实现了目标 2)它打破了“组件方法”。两种论点结合在一起,我猜拒绝一个可能会使 v-fragment 看起来很有用。
    • 我完全同意你的看法。但是在某些特定情况下(保持 HTML 结构正确),您需要使用片段或功能组件。
    • 看起来有两个版本不同的 repos "vue-fragment" npmjs.com/package/vue-fragment 和 "vue-fragments" npmjs.com/package/vue-fragments
    【解决方案5】:

    组件模板应该只包含一个根元素。如果您在多个元素上使用 v-if,请改用 v-else-if 链接它们。

    正确的做法是

    <template>
      <div> <!-- The root -->
        <p></p> 
        <p></p>
      </div>
    </template>
    

    错误的方法

    <template> <!-- No root Element -->
        <p></p> 
        <p></p>
    </template>
    

    多根组件

    解决该问题的方法是使用函数式组件,它们是您不必传递任何响应数据的组件,这意味着组件不会监视任何数据更改,也不会在父组件中的某些内容发生更改时自行更新。

    由于这是一种解决方法,它需要付出代价,功能组件没有任何生命周期挂钩传递给它,它们的实例更少,你不能再引用 this 并且所有内容都通过上下文传递。

    这是创建简单功能组件的方法。

    Vue.component('my-component', {
        // you must set functional as true
      functional: true,
      // Props are optional
      props: {
        // ...
      },
      // To compensate for the lack of an instance,
      // we are now provided a 2nd context argument.
      render: function (createElement, context) {
        // ...
      }
    })
    

    现在我们已经详细介绍了功能组件,让我们介绍如何创建多根组件,为此我将向您展示一个通用示例。

    <template>
     <ul>
         <NavBarRoutes :routes="persistentNavRoutes"/>
         <NavBarRoutes v-if="loggedIn" :routes="loggedInNavRoutes" />
         <NavBarRoutes v-else :routes="loggedOutNavRoutes" />
     </ul>
    </template>
    

    现在如果我们看一下 NavBarRoutes 模板

    <template>
     <li
     v-for="route in routes"
     :key="route.name"
     >
     <router-link :to="route">
     {{ route.title }}
     </router-link>
     </li>
    </template>
    

    我们不能做这样的事情,我们将违反单根组件限制

    解决方案 使这个组件正常工作并使用渲染

    {
    functional: true,
    render(h, { props }) {
     return props.routes.map(route =>
      <li key={route.name}>
        <router-link to={route}>
          {route.title}
        </router-link>
      </li>
     )
    }
    

    你已经创建了一个多根组件,Happy coding

    更多详情请参考:https://blog.carbonteq.com/vuejs-create-multi-root-components/

    【讨论】:

      【解决方案6】:

      我很困惑,因为我知道 VueJS 应该只包含 1 个根元素,但我仍然收到相同的“模板语法错误组件模板应该只包含一个根元素...”错误极其简单的组件。原来我只是将 拼错为 ,这在我复制和粘贴的几个文件中给了我同样的错误。总之,请检查您的语法是否存在组件中的任何拼写错误。

      【讨论】:

        【解决方案7】:

        而不是使用这个

        Vue.component('tabs', {
            template: `
                <div class="tabs">
                  <ul>
                    <li class="is-active"><a>Pictures</a></li>
                    <li><a>Music</a></li>
                    <li><a>Videos</a></li>
                    <li><a>Documents</a></li>
                  </ul>
                </div>
        
                <div class="tabs-content">
                  <slot></slot>
                </div>
            `,
        });
        

        你应该使用

        
        Vue.component('tabs', {
            template: `
              <div>
                <div class="tabs">
                  <ul>
                    <li class="is-active"><a>Pictures</a></li>
                    <li><a>Music</a></li>
                    <li><a>Videos</a></li>
                    <li><a>Documents</a></li>
                  </ul>
                </div>
        
                <div class="tabs-content">
                  <slot></slot>
                </div>
              </div>
            `,
        });
        
        

        【讨论】:

          【解决方案8】:

          只要确保你有一个根 div 并将所有内容都放在这个根中

            <div class="root">
              <!--and put all child here --!>
             <div class='child1'></div>
             <div class='child2'></div>
            </div>
          

          等等

          【讨论】:

            【解决方案9】:

            对于 vue 3,他们在模板语法中删除了这个约束:

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

            但它仍然存在于 JSX 语法中:

            不正确❌

            setup(props,{attrs}) {
            
            return ()=>(
                 <header>...</header>
                 <main  {..attrs}>...</main>
                 <footer>...</footer>
            )
            }
            

            正确✔

            setup(props,{attrs}) {
            
            return ()=>(
               <>
                 <header>...</header>
                 <main  {..attrs}>...</main>
                 <footer>...</footer>
               </>
            )
            }
            

            【讨论】:

              【解决方案10】:

              除了 Bert 和 blobmaster 的回复:

              如果您需要从 DOM 中删除根元素,您可以利用 css 并在根元素上使用 display: value

              【讨论】:

                【解决方案11】:

                有点误导性的错误。

                解决这个问题的原因是我有一个额外的&lt;/div&gt; 而没有打开&lt;div&gt;

                我在“div”上使用 Find/Replace 发现了它,它给出了一个奇数。

                【讨论】:

                • 如果你省略了开头的
                  ,那么错误就在眼前,因为你没有唯一的根元素。
                【解决方案12】:

                将所有内容包装在一个 div 中,它将解决问题。 例如,

                div

                ----div

                ----/div>

                ----div>

                ----/div>

                /div

                与 React.js 的概念类似

                【讨论】:

                  猜你喜欢
                  • 2017-09-20
                  • 2019-03-22
                  • 1970-01-01
                  • 2021-10-01
                  • 2021-02-28
                  • 2018-02-10
                  • 1970-01-01
                  • 2018-05-30
                  • 2017-03-18
                  相关资源
                  最近更新 更多