【VUE】异常解决:Avoid mutating a prop directly since the value will be overwritten ...

author: jwensh
date: 2021.07.03

1. 需求及报错

需求: 使用的场景是:A 组件中引用 B 组件,使用 v-model 给 B 传递参数,B 使用 props: { value } 接收内容,在 B 中根据逻辑直接修改并赋值 value, 事件触发后在浏览器 console 里看到报错,内容如下:

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"

2. 分析原因

  • 从报错内容上来看,我们改动了子组件中引用的父组件的变量,也就是 props 中的数据,是不能这么操作的;

  • 从提示的信息上看,使用 mutated 是否可行?

  • Vue2 中组件 props 中的数据只能单向流动,即只能从父组件通过组件的 DOM 属性 attribute 传递 props 给子组件,子组件只能被动接收父组件传递过来的数据,并且在子组件中,不能修改由父组件传来的 props 数据。

  • 组件内不能修改props的值,同时修改的值也不会同步到组件外层,即调用组件方不知道组件内部当前的状态是什么

2.1 这是什么原因造成?

  • vue1.x 版本中利用 propstwoWay.sync 绑定修饰符就可以实现 props 的双向数据绑定。

  • vue2.0 中移除了组件的 props 的双向数据绑定功能,如果需要双向绑定需要自己来实现。
    vue2.0 中组件的 props 的数据流动改为了只能单向流动,即只能由(父组件)通过组件的 v-bind:attributes 传递给(子组件),子组件只能被动接收父组件传递过来的数据,并在子组件内不能修改由父组件传递过来的 props 数据。

  • 官方文档解释:

prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。
虽然废弃了props的双向绑定对于整个项目整体而言是有利且正确的,但是在某些时候我们确实需要从组件内部修改props的需求

Vue2.0 中,实现组件属性的双向绑定方式(算不上是绑定了,算是异步修改), 可使用如下方法

  • v-model 指令或 .sync 修饰符
  • 将修改属性的方法通过 v-bind 传给子组件调用,子组件直接按方法使用即可
  • 将修改属性的方法通过 v-on 传递给子组件调用,使用 $emit() 实现回调修改
  • 或者使用 this.$parent 去修改

3. 解决方案: 使用 v-on 明确实现修改方式

也是为了代码可读性

不要直接修改从父组件传过来的 props 数据,在data函数中重新定义一个变量,将props数据数据赋值在data变量上,后面修改data变量即可。如下:

  • B 组件接受 A组件的参数
name: "B",
props: {
	father: {
		type: String,
		default: null
	}
}
data(){
  return{
    father_a : this.father
  }
}

如果想要监听 A 组件中传递过来的变量,从而修改 B 组件中的某个数据,可以使用 watch 这个监听函数来监听。如下:

name: "B",
props: {
	father: {
		type: String,
		default: null
	}
}
data(){
  return{
    father_a: this.father
    son: ''
  }
}
//监听函数,只要从父组件中传过来的father变量发生变化,
//子组件中定义的son变量就会赋值为“儿子”
watch:{
  father(val, valOld){
    this.son = "儿子"
  }
}

如果 B 想修改 A 传递过来的属性可以使用 $emit

  • A
<B :father="a" @change="changeA" />

<script>
export default {
	name: 'A',
	data() {
		return {
			a: 'A的变量'
		}
	},
	methods: {
		changeA(val) {
			this.a = val
		}
	}
}
</script>
  • B
<script>
export default {
	name: 'B',
	props: {
		father: {
			type: String,
			default: null
		}
	},
	data() {
	},
	watch:{
	  father(val, valOld){  // 这里也可以在 data 定义变量 并将 father 赋值给他, 在这里监控这个变量
	    // 这里做如果 father 变量变化了,子组件需要处理的逻辑
	  }
	},
	methods: {
		changeAFather() { // 谁来调用它? 是子组件的业务操作
			this.$emit('change', val)
		}
	}
}
</script>

至此,子组件内数据与父组件的数据的双向绑定,组件内外数据的同步:组件内部自己变了告诉外部,外部决定要不要变。

vue1.0 数据双向绑定在 vue2.0 版本中被抛弃了呢?通过案例也可以发现双向绑定的 props 代码多,不利于组件间的数据状态管理,尤其是在复杂的业务中更是如此,不易理解和阅读其中的逻辑,所以尽量不使用这种方式的双向绑定,过于复杂的数据处理使用vuex来进行数据管理。

4. 参考

  1. https://vuex.vuejs.org/zh-cn/
当在Vue中使用props传递数据时,应该避免直接修改props的值,因为props的值会在父组件重新渲染时被覆盖。相反,应该使用基于props值的data或computed属性来处理数据。这样做可以防止子组件意外修改父组件的状态,使数据流向更加清晰易懂。 如果你想修改父组件传递的props值,可以在子组件中定义一个data变量,并使用props的值初始化它。然后,通过使用$emit方法通知父组件去修改props的值。 以下是一个示例代码: ```javascript // 父组件 <template> <div> <child-component :facomment="comment" @update-comment="updateComment"></child-component> </div> </template> <script> export default { data() { return { comment: 'Hello World' } }, methods: { updateComment(newComment) { this.comment = newComment; } } } </script> // 子组件 <template> <div> <input v-model="localComment" @input="updateParentComment"> </div> </template> <script> export default { props: ['facomment'], data() { return { localComment: this.facomment } }, methods: { updateParentComment() { this.$emit('update-comment', this.localComment); } } } </script> ``` 在上面的示例中,父组件通过props将comment传递给子组件。子组件使用v-model绑定一个本地的localComment变量,并在输入框的输入事件中触发updateParentComment方法。该方法通过$emit方法将localComment的值传递给父组件的updateComment方法,从而实现了修改父组件props的值。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值