技术背景
在Vue 3开发中,当我们向组件传递属性时,可能会遇到 Extraneous non-props attributes
警告。这个警告表明传递给组件的非props属性无法自动继承,因为组件渲染的是片段或文本根节点。此问题通常在组件没有单一根元素时出现,了解如何解决该警告对于保证项目代码的健壮性和可读性非常重要。
实现步骤
问题复现
在示例中,父组件 views/Home.vue
向子组件 components/ItemProperties.vue
传递数组,同时传递了 class
属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <!-- views/Home.vue --> <template> <div class="wrapper"> <section v-for="(item, index) in items" :key="index" class="box"> <ItemProperties class="infobox-item-properties" :info="item.properties" /> </section> </div> </template> <script> import { ref } from 'vue' import { data } from '@/data.js' import ItemProperties from '@/components/ItemProperties.vue'
export default { components: { ItemProperties }, setup() { const items = ref(data)
return { items } } } </script>
|
子组件 ItemProperties.vue
渲染多个 <div>
元素,没有单一根元素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <!-- components/ItemProperties.vue --> <template> <div class="infobox-item-property" v-for="(object, index) in info" :key="index"> <span class="infobox-item-title">{{ object.name }}:</span> <span v-if="object.type === 'rating'"> <span v-for="(v, k) in object.value" :key="k">{{ object.icon }}</span> </span> <span v-else> <span>{{ object.value }}</span> </span> </div> </template>
<script> export default { props: { info: { type: Array, required: false, default: () => [ { name: '', value: '', type: 'string', icon: '' } ] } } } </script>
|
解决方法
方法一:移除多余属性或添加根元素
移除 class="infobox-item-properties"
,或者在子组件中添加一个根元素来包裹内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!-- components/ItemProperties.vue --> <template> <div> <div class="infobox-item-property" v-for="(object, index) in info" :key="index"> <span class="infobox-item-title">{{ object.name }}:</span> <span v-if="object.type === 'rating'"> <span v-for="(v, k) in object.value" :key="k">{{ object.icon }}</span> </span> <span v-else> <span>{{ object.value }}</span> </span> </div> </div> </template>
|
方法二:禁用属性继承
在子组件中设置 inheritAttrs: false
来禁用属性的自动继承:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| export default { name: "ItemProperties", inheritAttrs: false, props: { info: { type: Array, required: false, default: () => [ { name: '', value: '', type: 'string', icon: '' } ] } } }
|
在Vue 3.3+ 版本中,还可以使用 <script setup>
语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <script setup> defineOptions({ inheritAttrs: false }) </script>
<template> <div> <div class="infobox-item-property" v-for="(object, index) in info" :key="index"> <span class="infobox-item-title">{{ object.name }}:</span> <span v-if="object.type === 'rating'"> <span v-for="(v, k) in object.value" :key="k">{{ object.icon }}</span> </span> <span v-else> <span>{{ object.value }}</span> </span> </div> </div> </template>
|
方法三:声明属性
在子组件的 props
中声明传递的属性,例如声明 class
属性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| export default { props: ["class"], props: { info: { type: Array, required: false, default: () => [ { name: '', value: '', type: 'string', icon: '' } ] } } }
|
核心代码
以下是添加根元素解决问题的完整代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| <!-- views/Home.vue --> <template> <div class="wrapper"> <section v-for="(item, index) in items" :key="index" class="box"> <ItemProperties class="infobox-item-properties" :info="item.properties" /> </section> </div> </template> <script> import { ref } from 'vue' import { data } from '@/data.js' import ItemProperties from '@/components/ItemProperties.vue'
export default { components: { ItemProperties }, setup() { const items = ref(data)
return { items } } } </script>
<!-- components/ItemProperties.vue --> <template> <div> <div class="infobox-item-property" v-for="(object, index) in info" :key="index"> <span class="infobox-item-title">{{ object.name }}:</span> <span v-if="object.type === 'rating'"> <span v-for="(v, k) in object.value" :key="k">{{ object.icon }}</span> </span> <span v-else> <span>{{ object.value }}</span> </span> </div> </div> </template>
<script> export default { props: { info: { type: Array, required: false, default: () => [ { name: '', value: '', type: 'string', icon: '' } ] } } } </script>
|
最佳实践
- 在设计组件时,尽量保证组件有单一根元素,避免渲染片段。
- 对于需要传递属性的情况,明确声明
props
,避免不必要的属性传递。 - 合理使用
inheritAttrs
来控制属性的继承行为。
常见问题
为什么在Vue 2中没有这个问题?
在Vue 2中,组件必须渲染单一根元素,所以不会出现因多个根元素导致的属性无法继承问题。而Vue 3允许渲染多个根元素(片段),因此可能会触发此警告。
除了上述方法,还有其他解决办法吗?
还可以使用 v-bind="$attrs"
来手动处理属性传递,或者动态添加类名,如 myComponentEl.classList.add('awesome-class')
。同时,对于Vue 3.3+ 版本,也可以使用 defineProps
来解决警告:
1
| const props = defineProps(['class'])
|