Vue mutate prop 由带有同步修饰符的 v-bind 绑定

Vue mutate prop binded by v-bind with sync modifier

我的组件数据中有一个对象。现在,我只是使用 v-bind.sync 指令将对象的所有属性作为 prop 绑定到子组件。我正在使用内置 update 事件从子组件更新这些道具,但我仍然在控制台中收到 Avoid mutation props directly 错误。这是附加的最小示例。

父组件

<template>
  <div>
    <oslo v-bind.sync="data" />
  </div>
</template>

<script>

import Oslo from '@/components/Oslo.vue'

export default {
  components: {
    Oslo,
  },
  name: 'OsloParent',
  data() {
    return {
      data: {
        data: {
          name: 'Oslo name',
          access: 'admin'
        }
      },
    }
  },
}
</script>

子组件

<template>
  <div>
    <input type="text" v-model="name" @keyup="$emit('update:name', name)" />
    <input type="text" v-model="access" @keyup="$emit('update:access', access)" />
  </div>
</template>

<script>
export default {
  props: {
    name: String,
    access: String
  },
  name: 'Oslo',
}
</script>

这只是我为重现问题而创建的示例组件。实际组件应该通过双向绑定处理如此多的道具,这就是我使用 v-bind 指令和 sync 修饰符绑定数据的原因。这是来自控制台的 Vue 警告(最常见)。

[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: "name"

有什么改进的建议或让 Vue 对此特定情况发出警告吗?上面给出的组件按预期工作,但 Vue 抛出错误。

如果您有许多属性要从子组件侦听,您可以只传递一个对象并同步它而不是单个属性。请参阅以下示例:

Vue.config.productionTip = false
Vue.config.devtools = false

Vue.component('Oslo', {
  template: `
    <div>
    <input type="text" v-model="comp_name" @keyup="$emit('update:name', comp_name)" />
    <input type="text" v-model="comp_access" @keyup="$emit('update:access', comp_access)" />
  </div>
  `,
  props: {
    data: {
      name: String,
      access: String,
    }
  },
  data() {
    return {
      comp_name: this.data.name,
      comp_access: this.data.access
    }
  }
})

new Vue({
  el: '#app',
  data() {
    return {
      doc: {
        name: 'Oslo name',
        access: 'admin'
      }
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div>
    <span>---- {{ this.doc.name }}----</span>
    <span>---- {{ this.doc.access }}----</span>
    <oslo :data="this.doc" v-bind.sync="doc" />

  </div>
</div>

我发现你的例子有两个问题,可能会导致这个失败。

  1. 直接使用v-model到属性。使用 v-bind 而不是让它只显示。并使用 v-on:change 处理程序触发 $emit('update:propertyname', value) 并发送新值以在 object.

    上更新
  2. $emit 中发送的值似乎是空的,因此没有任何变化。请改用 $event.target.value

旁注:v-on:keyup 可能不是最适合收听的事件,因为输入也可以是 drag-and-dropped。那样的话听v-on:change比较好

仅使用 v-bind.sync 而不是 v-bind:propertyName.sync 时事件侦听器的注意事项:

如果您想从 parent 上的 child 组件监听 update:propertyName 事件,您必须使用 .capture 修饰符。否则 update 事件会被 child 组件上的 v-on:update:propertyName 捕获,并且不会冒泡到 parent.

因此,您可以在 <oslo> 标签上使用 v-on:update:name.capture="someMethod"。并且在 parent 的 methods 中有这个 someMethod。调用后,事件将在 child 组件上触发,该组件将更新 object,从而更新 属性.

总计:

let Oslo = {
  props: {
    name: String,
    access: String
  },
  name: 'Oslo',
  template: `<div>
    <input type="text" :value="name" @change="$emit('update:name', $event.target.value)" />
    <input type="text" :value="access" @change="$emit('update:access', $event.target.value)" />
  </div>`
}

new Vue({
  el: "#app",
  components: {
    Oslo,
  },
  data: {
    thedata: {
      name: 'Oslo name',
      access: 'admin'
    }
  },
  methods: {
    nameWillBeUpdated: function(v) {
      console.log('New value of name will be:', v);
      // After this, the `update:name` event handler of the
      // child component is triggered and the value will change.
    },
  },
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<span>{{this.thedata.name}} - {{this.thedata.access}}</span>
<oslo
    v-bind.sync="thedata"
    v-on:update:name.capture="nameWillBeUpdated"
/>
</div>