vue子组件修改props报错: [Vue warn]: ’Avoid mutating a prop directly since the value will be …‘解决方法

背景

vue开发时,新手经常会在控制台看到一个警告:vue.runtime.esm.js:4448 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"


这段警告大致意思是:
vue.runtime.esm.js:4448 [Vue 警告]:避免直接改变 prop,因为每当父组件重新渲染时,该值都会被覆盖。相反,请使用基于 prop 值的数据或计算属性。正在变异的道具:“value”

这涉及到一个vue框架的特性–单向数据流

Vue.js 是一个用于构建用户界面的渐进式框架,它设计时考虑了易用性和灵活性。在 Vue.js 中,数据绑定是核心特性之一,它允许开发者将数据从组件的模型同步到视图层。Vue 的数据绑定机制遵循单向数据流的原则,这意味着数据的流动是单向的,从父组件流向子组件。

单向数据流的概念:

  1. 数据从父组件流向子组件:在 Vue 组件中,父组件可以通过 props 将数据传递给子组件。子组件不能直接修改通过 props 接收到的 props 数据,但可以将 props 数据复制到自己的 data 中,然后进行修改。

  2. 事件从子组件流向父组件:子组件不能直接修改父组件的数据,但可以通过自定义事件(使用 $emit)将数据变化通知给父组件。父组件监听这些事件,并根据需要更新自己的状态。

  3. 避免数据的双向绑定问题:单向数据流有助于避免组件之间的数据同步问题,使得数据的流向更加清晰和可预测。

单向数据流的优点:

  • 可预测性:数据流动的单一方向使得状态变化更容易追踪。
  • 易于调试:由于数据流的清晰性,开发者可以更容易地定位问题。
  • 组件的独立性:组件之间通过 props 和事件进行通信,减少了组件间的耦合。

实现单向数据流的模式:

  • Props:父组件通过 props 将数据传递给子组件。
  • 事件:子组件通过 $emit 发出事件,父组件监听这些事件并响应。
  • 计算属性:基于 props 的数据,子组件可以创建计算属性来处理数据。
  • 方法:子组件可以定义方法来响应事件,但这些方法不能直接修改 props。

示例:

<template>
  <div class="inputComp-container">
    <h4>子组件</h4>
    <el-input v-model="value" placeholder="请输入内容"></el-input>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  data () {
    return {

    }
  }
}
</script>
<template>
  <div>
    <h3>父组件</h3>
    <span>{{ word }}</span>
    <inputComp :value="word"></inputComp>
  </div>
</template>

<script>
import inputComp from './inputComp'
export default {
  components: {
    inputComp
  },
  data () {
    return {
      word: ''
    }
  }
}
</script>

这么写,子组件会直接修改props传入的value属性,从而打破了单向数据流,vue就会在控制台报警告。

解决方法1(emit事件)

子组件:

<template>
  <div class="inputComp-container">
    <h4>子组件</h4>
    <el-input v-model="inputWord" @change="$emit('change', inputWord)" placeholder="请输入内容"></el-input>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      inputWord: ''
    }
  }
}
</script>

父组件:

<template>
  <div>
    <h3>父组件</h3>
    <span>{{ word }}</span>
    <inputComp :value="word" @change="($event) => word = $event"></inputComp>
  </div>
</template>

<script>
import inputComp from './inputComp'
export default {
  components: {
    inputComp
  },
  data () {
    return {
      word: ''
    }
  }
}
</script>

解决方法2(利用v-model语法糖)

本质和上面的方法一致,通过emit事件给父组件,去修改父组件的值i,从而不会打破单向数据流。
只需要子组件emit特定的input时间,父组件即可直接写v-model,不用单独写对input事件的处理。
子组件:

<template>
  <div class="inputComp-container">
    <h4>子组件</h4>
    <el-input v-model="inputWord" @change="$emit('input', inputWord)" placeholder="请输入内容"></el-input>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      inputWord: ''
    }
  }
}
</script>

父组件:

<template>
  <div>
    <h3>父组件</h3>
    <span>{{ word }}</span>
    <inputComp v-model="word"></inputComp>
  </div>
</template>

<script>
import inputComp from './inputComp'
export default {
  components: {
    inputComp
  },
  data () {
    return {
      word: ''
    }
  }
}
</script>

解决方法3(.sync 修饰符)推荐

vue官方也提供了一个修饰符去解决这个问题:

官方介绍:https://v2.cn.vuejs.org/v2/guide/components-custom-events.html#sync-%E4%BF%AE%E9%A5%B0%E7%AC%A6

用法代码:

子组件:

<template>
  <div class="inputComp-container">
    <h4>子组件</h4>
    <el-input v-model="inputWord" @change="$emit('update:value', inputWord)" placeholder="请输入内容"></el-input>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      inputWord: ''
    }
  }
}
</script>

父组件:

<template>
  <div>
    <h3>父组件</h3>
    <span>{{ word }}</span>
    <inputComp :value.sync="word"></inputComp>
  </div>
</template>

<script>
import inputComp from './inputComp'
export default {
  components: {
    inputComp
  },
  data () {
    return {
      word: ''
    }
  }
}
</script>

以上解决方案,均适用于Vue2版本,部分方法可能vue3有变化,具体看实际效果。

  • 27
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当我们在组件中直接修改组件传递下来的prop值时,会出现警告信息"Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.",意思是直接修改prop值会导致父组件重新渲染时该值被覆盖,应该使用一个基于prop值的data或computed属性来代替直接修改prop值。这是因为Vue中的单向数据流规定了prop只能由父组件传递给组件组件不能直接修改prop值,否则会破坏单向数据流的规则,导致数据流混乱,难以维护。 解决方法有两种: 1. 在组件中使用一个基于prop值的data或computed属性来代替直接修改prop值。 2. 在组件中使用$emit方法触发一个自定义事件,将修改后的值通过事件参数传递给父组件,在父组件修改prop值。 以下是两种解决方法的代码示例: 1. 使用一个基于prop值的data或computed属性来代替直接修改prop值 ```javascript // 组件B props: { value: { type: String, required: true } }, data() { return { // 使用一个基于prop值的data属性来代替直接修改prop值 inputValue: this.value } }, methods: { handleChange() { // 在组件修改inputValue的值 this.inputValue = 'new value' // 触发input事件,将修改后的值通过事件参数传递给父组件 this.$emit('input', this.inputValue) } } // 父组件A中引用组件B <template> <div> <B v-model="value"></B> </div> </template> <script> export default { data() { return { value: 'initial value' } } } </script> ``` 2. 在组件中使用$emit方法触发一个自定义事件,将修改后的值通过事件参数传递给父组件,在父组件修改prop值。 ```javascript // 组件B props: { value: { type: String, required: true } }, methods: { handleChange() { // 在组件中触发一个自定义事件,将修改后的值通过事件参数传递给父组件 this.$emit('update:value', 'new value') } } // 父组件A中引用组件B <template> <div> <B :value="value" @update:value="handleUpdate"></B> </div> </template> <script> export default { data() { return { value: 'initial value' } }, methods: { handleUpdate(newValue) { // 在父组件修改prop值 this.value = newValue } } } </script> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值