在将 Vue 2 组件迁移到 Vue 3 时,控制台经常会出现如下警告:- [Vue warn]: Extraneous non-props attributes (modelValue) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.
复制代码 这个警告的本质是 Vue 3 中 v-model 的语法变化,与组件存在多根节点(Fragment)共同导致。Vue 3 中组件上的 v-model 默认绑定 prop 为 modelValue,触发事件为 update:modelValue;而旧组件内部定义的 prop 是 value,触发事件是 input,仍然沿用 Vue 2 的写法。父组件使用 v-model 时会向子组件传入 modelValue,但子组件 props 中没有声明该属性,于是 Vue 把它当作非 prop 属性处理。由于组件模板包含了多个根节点,无法自动继承这些未声明属性,因此输出警告。
下面提供两种方案,分别对应“彻底升级组件”和“保留旧 prop 名兼容”两种场景。
方案一:将组件改造为符合 Vue 3 标准的 v-model(推荐)
这个方法把组件的 value 改成 modelValue,并将内部触发的 input 事件改为 update:modelValue,让组件在语法上符合 Vue 3 默认约定。
1. 修改组件模板中存在 value 的绑定位置:- <template>
- <!-- 将 value 改为 modelValue -->
- <page-meta :page-style="modelValue ? 'overflow:hidden;height:100vh;position:fixed;' : ''" />
- <view v-if="modelValue">
- <!-- 内部其他原有代码保持不变 -->
- <view class="dialog-model" @click="handleMaskClose" @touchmove.stop.prevent="()=>{}" @wheel.stop.prevent="()=>{}">
- <!-- ... -->
- </view>
- </view>
- </template>
复制代码
2. 修改 script 中的 props、emits 和事件触发逻辑:- <script>
- export default {
- name: "u-dailog",
- // 建议加上 inheritAttrs: false,防止后续传入 class/style 时因多根节点再次出现继承警告
- inheritAttrs: false,
- props: {
- // 将 value 改为 modelValue
- modelValue: {
- type: Boolean,
- default: false
- },
- // ... 其他 props 保持不变
- },
- // 声明 emits 是 Vue 3 推荐做法
- emits: ['update:modelValue', 'cancel', 'confirm'],
- data() {
- return {}
- },
- // ... computed 保持不变
- methods: {
- onCancel() {
- // 将 "input" 改为 "update:modelValue"
- this.$emit("update:modelValue", false)
- this.$emit('cancel')
- },
- onConfirm() {
- // 将 "input" 改为 "update:modelValue"
- this.$emit("update:modelValue", false)
- this.$emit('confirm')
- },
- handleMaskClose() {
- this.maskClose && this.onCancel()
- }
- }
- }
- </script>
复制代码
完成以上修改后,父组件原有的写法:- <u-dialog v-model="showDialog"></u-dialog>
复制代码 完全不用改动,即可在 Vue 3 中正常工作。
方案二:保留组件的 value 属性,修改父组件用法
如果不想修改组件内部 prop 名称,可以借助 Vue 3 v-model 自定义参数的能力,明确指定绑定到 value。
1. 父组件调用处修改为:- <u-dialog v-model:value="showDialog"></u-dialog>
复制代码
2. 子组件 script 中需要把触发 input 事件的逻辑改为触发 update:value 事件:- methods: {
- onCancel() {
- // 将 "input" 改为 "update:value"
- this.$emit("update:value", false)
- this.$emit('cancel')
- },
- onConfirm() {
- // 将 "input" 改为 "update:value"
- this.$emit("update:value", false)
- this.$emit('confirm')
- },
- handleMaskClose() {
- this.maskClose && this.onCancel()
- }
- }
复制代码 属性名保持 value 不变,但事件名必须符合 Vue 3 的更新约定,否则绑定无法同步。
这种问题在 Vue 2 迁移到 Vue 3 时非常典型。建议优先采用方案一,把组件彻底升级为 Vue 3 标准写法。同时,对有多根节点的组件主动设置 inheritAttrs: false,可以避免其他非 prop 属性(比如 class、style)在 Fragment 场景下再次触发警告。若项目内大量组件都存在类似问题,方案二可以在不重构组件的前提下快速适配,但会留下旧式命名的痕迹,后续依然需要统一治理。 |