如何“修改”Vue中的prop

11,314 阅读3分钟

在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,也可以使用这种方法验证,大家可以自行验证,就不再赘述~

感谢阅读