在Vue中,prop是可以接受由父组件传递给子组件的属性,但prop无法进行修改的(所以我加了引号,嘿嘿)。
如果prop被强制修改,会有警告⚠️
❝[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~」
当然,如果你对以上的话一脸懵逼可以看看官网文档关于Prop的内容
1.使用父子组件传值修改
上面的警告大概意思说:单独在子组件中修改数值,在父组件重新渲染更新的时候还是传一样的值进去,造成一个重写的情况。
说得白话一点:哪个组件定义数值,应该哪个组件定义方法(函数)进行修改。
和Vuex的状态管理类似,为什么state中的数值只能由mutation进行修改操作,最核心的原因——可回溯。
只由一个“管理员”进行修改操作,避免多个不同身份进行修改操作,这样就不会“乱套”了,而在状态追溯时候只要看mutation做了什么操作,这样就会一目了然。
那自然,如果传递给子组件的prop数据是在父组件上定义,那修改的prop的数据的方法(函数)可以在父组件中定义。
<!--父组件-->
<template>
<div>
<Child :propNum="num" @propFun="changeNum"/>
</div>
</template>
<script>
// 引入 src/components/Child
// 引入要根据文件目录进行修改
import Child from '@/components/Child'
export default {
components: { // 注册组件
Child
},
data () {
return { // 定义num数值
num: 0
}
},
methods: { // 定义修改数值方法
changeNum () {
this.num = this.num + 1
}
}
}
</script>
<!--子组件-->
<template>
<div>
<span>
{{propNum}}
</span>
<button type="button" @click="onClick">
add
</button>
</div>
</template>
<script>
export default {
props: {
propNum: { // 接受父子组件传递的属性
type: Number,
default: 0
}
},
methods: {
onClick () { // 在点击按钮触发,父组件传递给子组件的方法
this.$emit('propFun')
}
}
}
</script>
2.使用.sync修饰
「注意!是vue 2.30以上才能使用,因为刚开始的vue2.0废除的这个修饰符 官网文档」
这里也写一个和上述类似的例子
<!--父组件-->
<template>
<div>
<Child :propNum.sync="num"/>
</div>
</template>
<script>
// 引入 src/components/Child
// 引入要根据文件目录进行修改
import Child from '@/components/Child'
export default {
components: {
Child
},
data () {
return {
num: 0
}
}
}
</script>
<!--子组件-->
<template>
<div>
<span>
{{propNum}}
</span>
<button type="button" @click="onClick">
add
</button>
</div>
</template>
<script>
export default {
props: {
propNum: {
type: Number,
default: 0
}
},
methods: {
onClick () {
const num = this.propNum + 1 // 定义num数值
this.$emit('update:propNum', num) // 传递更新数值
}
}
}
</script>
3.使用自定义组件的v-model
和vue封装好v-mode的不同,以下使用的是自定义组件的v-model
「当然还是要注意使用版本:2.20+」
<!--父组件-->
<template>
<div>
<Child v-model="num"/>
</div>
</template>
<script>
// 引入 src/components/Child
// 引入要根据文件目录进行修改
import Child from '@/components/Child'
export default {
components: {
Child
},
data () {
return {
num: 0
}
}
}
</script>
<!--子组件-->
<template>
<div>
<span>
{{propNum}}
</span>
<button type="button" @click="onClick">
add
</button>
</div>
</template>
<script>
export default {
model: {
prop: 'propNum', // v-model选择prop中传递名
event: 'changeNum' // 定义事件
},
props: {
propNum: {
type: Number,
default: 0
}
},
methods: {
onClick () {
const num = this.propNum + 1
this.$emit('changeNum', num) // 使用$emit触发事件并传值
}
}
}
</script>
4.总结
以上三种方法都可以“变相修改”props,其中使用父子组件传值是引用“定义状态同时定义修改状态方法”,而后两中.sync和v-model则是使用“双向绑定”。
似乎第一种好像还可以理解,但后两种又怎么验证呢?,其实只在父和子组件加上使用watch,如在使用.sync修饰符上加watch:
<!--父组件-->
<template>
<div>
<Child :propNum.sync="num"/>
</div>
</template>
<script>
// 引入 src/components/Child
// 引入要根据文件目录进行修改
import Child from '@/components/Child'
export default {
components: {
Child
},
data () {
return {
num: 0
}
},
watch: { // 监听父组件num属性
num: {
handler () {
console.log('父组件num', this.num)
}
}
}
}
</script>
<!--子组件-->
<template>
<div>
<span>
{{propNum}}
</span>
<button type="button" @click="onClick">
add
</button>
</div>
</template>
<script>
export default {
props: {
propNum: {
type: Number,
default: 0
}
},
methods: {
onClick () {
const num = this.propNum + 1 // 定义num数值
this.$emit('update:propNum', num) // 传递更新数值
}
},
watch: { // 监听子组件propNum属性
propNum: {
handler () {
console.log('子组件propNum', this.propNum)
}
}
}
}
</script>
运行,点击add按钮就有以下结果
类似使用自定义组件但v-model,也可以使用这种方法验证,大家可以自行验证,就不再赘述~
感谢阅读