Vue js 错误:组件模板应该只包含一个根元素

IT技术 javascript node.js laravel vue.js
2021-02-15 07:31:18

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

当我运行时$ npm run watch,出现以下错误:

“Webpack 正在监视文件......

95% 排放

错误 无法编译,出现 1 个错误
19:42:29

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

(Emitted value 而不是 Error 的实例)Vue 模板语法错误:

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

@ ./resources/assets/js/components/AvatarUpload.vue 5:2-181 @ ./resources/assets/js/app.js @ multi ./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>

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

6个回答

注意此答案仅适用于 Vue 2.x 版。版本 3 取消了这个限制

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

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

你需要一个。

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

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

基本上在 Vue中,你的模板中必须只有一个根元素

我在 VS Code 中编辑了 Vue 3 项目,而 Vetur 扩展正在为它哭泣:( 我是 Vue 的新手,尤其是 Vue 3,这个扩展比帮助​​更令人困惑
2021-04-20 07:31:18
这个限制在 Vue 3 fwiw 中消失了。不幸的是,我现在找不到这个来源,但他们在 VueConf 2020 上宣布了它。
2021-05-03 07:31:18
2021-05-13 07:31:18

更完整的答案: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>
看起来有两个 repos "vue-fragment" npmjs.com/package/vue-fragment和 "vue-fragments" npmjs.com/package/vue-fragments具有不同的版本控制
2021-04-18 07:31:18
我完全同意你的看法。但是在某些特定情况下(保持 HTML 结构正确),您需要使用片段或功能组件。
2021-04-25 07:31:18
恕我直言,我有两个反对 v-fragment 的论点:1)这是一个不必要的依赖,因为你实现了内置功能组件的目标 2)它打破了“组件方法”。两个论点结合在一起,我猜拒绝一个可能会使 v-fragment 看起来有用。
2021-05-14 07:31:18

您需要将所有 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>

如果出于任何原因,您不想添加包装器(在我的第一种情况下是用于<tr/>组件),您可以使用功能组件。

components/MyCompo.vue一个components/MyCompo文件夹中将有几个文件,而不是一个

  • 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) } )
    ]; 
  }
};

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

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

很快,答案是:不。但是你总是可以通过 DOM api 做任何你想做的事。您不应该使用模板生成的 DOM,它会导致与虚拟 DOM(或充当的事物)不同步。由于更改了模板生成的 dom,您可能会再次生成模板,因此您的模板将生成两次。
2021-04-27 07:31:18
嗨,有没有其他方法可以防止在呈现模板后显示根 div?
2021-04-28 07:31:18

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

正确的做法是

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

错误的做法

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

多根组件

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

由于这是一个有代价的解决方法,功能组件没有任何生命周期钩子传递给它,它们的实例更少,你不能再引用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/