我开始了https://laracasts.com/series/learning-vue-step-by-step系列。我在Vue、Laravel和AJAX的课程上停了下来,出现了这个错误:

Vue .js:2574 [Vue警告]:避免直接改变道具,因为每当父组件重新呈现时,该值将被覆盖。相反,应该使用基于道具值的数据或计算属性。道具被突变:"list"(在组件中找到)

我在main.js中有这段代码

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    created() {
        this.list = JSON.parse(this.list);
    }
});
new Vue({
    el: '.container'
})

我知道,当我覆盖列表道具时,问题是在created(),但我是Vue的新手,所以我完全不知道如何修复它。有人知道如何(请解释为什么)解决这个问题吗?


当前回答

您应该始终避免在vue或任何其他框架中改变道具。您可以采用的方法是将其复制到另一个变量中。

为例。 //而不是替换this的值。List使用不同的变量

这一点。new_data_variable = JSON.parse(this.list)

其他回答

Vue3有一个很好的解决方案。花了好几个小时才到那里。但是效果很好。

在父模板上

<user-name
  v-model:first-name="firstName"
  v-model:last-name="lastName"
></user-name>

子组件

app.component('user-name', {
      props: {
      firstName: String,
      lastName: String
     },
template: `
   <input 
  type="text"
  :value="firstName"
  @input="$emit('update:firstName', 
 $event.target.value)">

<input
  type="text"
  :value="lastName"
   @input="$emit('update:lastName', 
    $event.target.value)">
  `
})

这是唯一能双向结合的解。我喜欢前两个答案,以良好的方式使用同步和发射更新事件,并计算属性getter setter,但这是一个工作要做的,我不喜欢这么努力工作。

Vue只是警告您:您更改了组件中的道具,但当父组件重新呈现时,“list”将被覆盖,您将丢失所有更改。所以这样做是危险的。

使用computed属性,如下所示:

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    computed: {
        listJson: function(){
            return JSON.parse(this.list);
        }
    }
});

单向数据流, 根据https://v2.vuejs.org/v2/guide/components.html,该组件遵循单向 数据流, 所有的道具在子属性和父属性之间形成了一个单向的向下绑定,当父属性更新时,它将向下流到子属性,而不是相反,这可以防止子组件意外地改变父属性,这可能会使你的应用程序的数据流更难理解。

此外,每次父组件更新所有的道具 子组件中的值将被刷新为最新值。这意味着您不应该试图改变子组件中的道具。如果你这样做了,vue会在 控制台。

通常有两种情况很容易对道具进行变异: prop用于传入一个初始值;子组件希望随后将其用作本地数据属性。 道具作为需要转换的原始值传递进来。 这些用例的正确答案是: 定义一个本地数据属性,使用prop的初始值作为其初始值:

props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}

定义一个计算属性,从prop的值计算:

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

当TypeScript是你的首选lang。的发展

<template>
<span class="someClassName">
      {{feesInLocale}}
</span>
</template>  



@Prop({default: 0}) fees: any;

// computed are declared with get before a function
get feesInLocale() {
    return this.fees;
}

而不是

<template>
<span class="someClassName">
      {{feesInLocale}}
</span>
</template>  



@Prop() fees: any = 0;
get feesInLocale() {
    return this.fees;
}

答案很简单,您应该通过将值分配给一些局部组件变量(可以是数据属性,用getter、setter或观察者计算)来打破直接的prop突变。

这里有一个使用监视器的简单解决方案。

<template>
  <input
    v-model="input"
    @input="updateInput" 
    @change="updateInput"
  />

</template>

<script>
  export default {
  props: {
    value: {
      type: String,
      default: '',
    },
  },
  data() {
    return {
      input: '',
    };
  },
  watch: {
    value: {
      handler(after) {
        this.input = after;
      },
      immediate: true,
    },
  },
  methods: {
    updateInput() {
      this.$emit('input', this.input);
    },
  },
};
</script>

我用它来创建任何数据输入组件,它工作得很好。从父元素发送的任何新数据(v-model(ed))都将被值监视器监视并分配给输入变量,一旦接收到输入,我们就可以捕捉该操作并向父元素发出输入,表明数据是从表单元素输入的。