Vue 2 - 变异道具vue-warn

问题描述 投票:117回答:17

我开始了https://laracasts.com/series/learning-vue-step-by-step系列。我停止了Vue,Laravel和AJAX这个错误:

vue.js:2574 [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: "list" (found in component <task>)

我在main.js中有这段代码

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

我知道当我覆盖列表道具时问题出在created()中,但我是Vue的新手,所以我完全不知道如何解决它。任何人都知道如何(并请解释原因)来修复它?

javascript vue.js vuejs2
17个回答
183
投票

这与mutating a prop locally is considered an anti-pattern in Vue 2的事实有关

如果你想在本地改变一个道具,你现在应该做的是在你的data中声明一个使用props值作为其初始值然后改变副本的字段:

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

你可以在Vue.js official guide上阅读更多相关信息


注1:请注意you should not use the same name for your prop and data,即:

data: function () { return { list: JSON.parse(this.list) } // WRONG!!

注2:自I feel there is some confusion关于props和反应性,我建议你看看this线程


3
投票

如果你想改变道具 - 使用对象。

<component :model="global.price"></component>

零件:

props: ['model'],
methods: {
  changeValue: function() {
    this.model.value = "new value";
  }
}

3
投票

你需要像这样添加计算方法

component.vue

props: ['list'],
computed: {
    listJson: function(){
        return JSON.parse(this.list);
    }
}

3
投票

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

此外,每次更新父组件时,子组件中的所有道具都将使用最新值进行刷新。这意味着您不应该尝试改变子组件内的prop。如果你这样做.vue会在控制台中警告你。

通常有两种情况,它很容易改变道具:道具用于传递初始值;子组件之后想要将它用作本地数据属性。 prop是作为需要转换的原始值传递的。这些用例的正确答案是:定义使用prop的初始值作为其初始值的本地数据属性:

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

定义根据prop的值计算的计算属性:

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

2
投票

Vue.js道具不会被改变,因为它被认为是Vue中的反模式。

您需要采取的方法是在组件上创建一个引用list的原始prop属性的数据属性

props: ['list'],
data: () {
  return {
    parsedList: JSON.parse(this.list)
  }
}

现在,通过组件的data属性引用和变异传递给组件的列表结构:-)

如果您希望做的不仅仅是解析列表属性,那么请使用Vue组件'computed属性。这允许您对道具进行更深入的突变。

props: ['list'],
computed: {
  filteredJSONList: () => {
    let parsedList = JSON.parse(this.list)
    let filteredList = parsedList.filter(listItem => listItem.active)
    console.log(filteredList)
    return filteredList
  }
}

上面的例子解析你的列表道具并将其过滤到只有活动的列表文本,将其记录下来用于schnitts和giggles并返回它。

注意:datacomputed属性在模板中引用相同,例如

<pre>{{parsedList}}</pre>

<pre>{{filteredJSONList}}</pre>

可以很容易地认为需要调用computed属性(作为一种方法)......它不需要


2
投票
Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    computed: {
      middleData() {
        return this.list
      }
    },
    watch: {
      list(newVal, oldVal) {
        console.log(newVal)
        this.newList = newVal
      }
    },
    data() {
      return {
        newList: {}
      }
    }
});
new Vue({
    el: '.container'
})

也许这将满足您的需求。


2
投票

添加到最佳答案,

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

通过数组设置道具是用于开发/原型设计,在生产中确保设置道具类型(https://vuejs.org/v2/guide/components-props.html)并设置默认值,以防道具未被父项填充,如此。

Vue.component('task', {
    template: '#task-template',
    props: {
      list: {
        type: String,
        default() {
          return '{}'
        }
      }
    },
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

这样你至少在mutableList中得到一个空对象而不是JSON.parse错误,如果它是未定义的。


0
投票

Vue.js认为这是一种反模式。例如,声明和设置一些道具,如

this.propsVal = 'new Props Value'

因此,要解决此问题,您必须从props获取值到数据或Vue实例的计算属性,如下所示:

props: ['propsVal'],
data: function() {
   return {
       propVal: this.propsVal
   };
},
methods: {
...
}

这肯定会奏效。


0
投票

除上述内容外,对于有以下问题的其他人:

“如果不需要道具值,因此不总是返回,传递的数据将返回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>

28
投票

Vue只是警告你:你改变组件中的prop,但是当父组件重新渲染时,“list”将被覆盖,你将丢失所有的更改。所以这样做很危险。

使用计算属性,如下所示:

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

17
投票

如果你正在使用Lodash,你可以在返回之前克隆道具。如果您在父项和子项上修改该prop,则此模式很有用。

假设我们在组件网格上有道具列表。

在父组件中

<grid :list.sync="list"></grid>

在子组件中

props: ['list'],
methods:{
    doSomethingOnClick(entry){
        let modifiedList = _.clone(this.list)
        modifiedList = _.uniq(modifiedList) // Removes duplicates
        this.$emit('update:list', modifiedList)
    }
}

17
投票

道具失败,事件发生了。这是Vue的模式。关键是,如果你试图改变从父母传递的道具。它不起作用,它只是被父组件重复覆盖。子组件只能发出事件来通知父组件做某事。如果您不喜欢这些限制,可以使用VUEX(实际上这种模式会吸引复杂的组件结构,您应该使用VUEX!)


11
投票

您不应该更改子组件中props的值。如果你真的需要改变它,你可以使用.sync。像这样

<your-component :list.sync="list"></your-component>

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

11
投票

Vue模式很简单:props down和events up。这听起来很直接,但在编写自定义组件时很容易忘记。

从Vue 2.2.0开始,您可以使用v-model(使用computed properties)。我发现这种组合在组件之间创建了一个简单,干净,一致的界面:

  • 传递给您的组件的任何props仍然是被动的(即,它没有被克隆,也不需要watch函数来在检测到更改时更新本地副本)。
  • 更改会自动发送给父级。
  • 可与多级组件一起使用。

计算属性允许单独定义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属性定义哪个propv-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发出变化,它将在set()上适当地调用listLocal,从而将事件传播到顶层。类似地,EditTask组件也可以使用v-model调用其他子组件(例如表单元素)。


6
投票

根据VueJs 2.0,你不应该改变组件内的prop。他们只是由父母变异。因此,您应该在具有不同名称的数据中定义变量,并通过观察实际道具来更新它们。如果父级更改了列表道具,您可以解析它并将其分配给mutableList。这是一个完整的解决方案。

Vue.component('task', {
    template: ´<ul>
                  <li v-for="item in mutableList">
                      {{item.name}}
                  </li>
              </ul>´,
    props: ['list'],
    data: function () {
        return {
            mutableList = JSON.parse(this.list);
        }
    },
    watch:{
        list: function(){
            this.mutableList = JSON.parse(this.list);
        }
    }
});

它使用mutableList来渲染您的模板,从而使您的列表支持在组件中保持安全。


5
投票

不要直接在components中更改道具。如果你需要更改,它会设置一个新属性,如下所示:

data () {
    return () {
        listClone: this.list
    }
}

并更改listClone的值。


4
投票

我也遇到过这个问题。在我使用$on$emit后警告消失了。这类似于使用$on$emit建议将数据从子组件发送到父组件。

© www.soinside.com 2019 - 2024. All rights reserved.