我开始了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的新手,所以我完全不知道如何修复它。有人知道如何(请解释为什么)解决这个问题吗?


当前回答

除上述问题外,如有下列问题:

如果props值不是必需的,因此不总是返回,则传递的数据将返回undefined(而不是空)。这可能会混乱<select>默认值,我通过检查是否在beforeMount()设置的值来解决它(如果没有设置它)如下:

JS:

export default {
        name: 'user_register',
        data: () => ({
            oldDobMonthMutated: this.oldDobMonth,
        }),
        props: [
            'oldDobMonth',
            'dobMonths', //Used for the select loop
        ],
        beforeMount() {
           if (!this.oldDobMonth) {
              this.oldDobMonthMutated = '';
           } else {
              this.oldDobMonthMutated = this.oldDobMonth
           }
        }
}

Html:

<select v-model="oldDobMonthMutated" id="dob_months" name="dob_month">

 <option selected="selected" disabled="disabled" hidden="hidden" value="">
 Select Month
 </option>

 <option v-for="dobMonth in dobMonths"
  :key="dobMonth.dob_month_slug"
  :value="dobMonth.dob_month_slug">
  {{ dobMonth.dob_month_name }}
 </option>

</select>

其他回答

Vue模式是向下的道具和向上的事件。这听起来很简单,但在编写定制组件时很容易忘记。

从Vue 2.2.0开始,您可以使用v-model(带有计算属性)。我发现这种组合可以在组件之间创建一个简单、干净和一致的接口:

传递给组件的任何道具都保持响应性(即,它不是克隆的,也不需要监视函数在检测到更改时更新本地副本)。 更改会自动发出到父节点。 可以与多级组件一起使用。

计算属性允许分别定义setter和getter。这允许Task组件被重写如下:

Vue.component('Task', {
    template: '#task-template',
    props: ['list'],
    model: {
        prop: 'list',
        event: 'listchange'
    },
    computed: {
        listLocal: {
            get: function() {
                return this.list
            },
            set: function(value) {
                this.$emit('listchange', value)
            }
        }
    }
})  

model属性定义了哪个道具与v-model相关联,以及在发生更改时将触发哪个事件。然后你可以从父组件中调用这个组件,如下所示:

<Task v-model="parentList"></Task>

listLocal计算属性在组件中提供了一个简单的getter和setter接口(可以把它看作一个私有变量)。在# Task -template中,你可以呈现listLocal,它将保持响应性(即,如果parentList发生变化,它将更新Task组件)。你也可以通过调用setter来改变listLocal(例如this。listLocal = newList),它将把更改发送给父类。

这种模式的优点在于,您可以将listLocal传递给Task的子组件(使用v-model),并且来自子组件的更改将传播到顶层组件。

例如,假设我们有一个单独的EditTask组件,用于对任务数据进行某种类型的修改。通过使用相同的v-model和计算属性模式,我们可以将listLocal传递给组件(使用v-model):

<script type="text/x-template" id="task-template">
    <div>
        <EditTask v-model="listLocal"></EditTask>
    </div>
</script>

如果EditTask发出更改,它将适当地调用listLocal上的set(),从而将事件传播到顶层。类似地,EditTask组件也可以使用v-model调用其他子组件(比如表单元素)。

一个潜在的解决方案是使用全局变量。

import { Vue } from "nuxt-property-decorator";

export const globalStore = new Vue({
  data: {
    list: [],
  },
}

export function setupGlobalsStore() {
  Vue.prototype.$globals = globalStore;
}

然后你可以用:

$globals.list

任何你需要变异或呈现它的地方。

下面是一个小吃店组件,当我把小吃店变量直接输入v-model,就像这样,如果将工作,但在控制台中,它会给出一个错误

避免直接改变道具,因为每当父组件重新呈现时,该值将被覆盖。相反,应该使用基于道具值的数据或计算属性。

<template>
        <v-snackbar v-model="snackbar">
        {{ text }}
      </v-snackbar>
</template>

<script>
    export default {
        name: "loader",
     
        props: {
            snackbar: {type: Boolean, required: true},
            text: {type: String, required: false, default: ""},
        },
      
    }
</script>

消除这种变异错误的正确方法是使用监测器

<template>
        <v-snackbar v-model="snackbarData">
        {{ text }}
      </v-snackbar>
</template>

<script>
/* eslint-disable */ 
    export default {
        name: "loader",
         data: () => ({
          snackbarData:false,
        }),
        props: {
            snackbar: {type: Boolean, required: true},
            text: {type: String, required: false, default: ""},
        },
        watch: { 
        snackbar: function(newVal, oldVal) { 
          this.snackbarData=!this.snackbarDatanewVal;
        }
      }
    }
</script>

在加载小吃店的主组件中,你可以执行这段代码

<loader :snackbar="snackbarFlag" :text="snackText"></loader>

这对我很有用

单向数据流, 根据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()
  }
}

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

使用computed属性,如下所示:

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