Javascript Vue 2 - 改变道具 vue-warn

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39868963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 23:00:17  来源:igfitidea点击:

Vue 2 - Mutating props vue-warn

javascriptvue.jsvuejs2

提问by Dariusz Chowański

I started https://laracasts.com/series/learning-vue-step-by-stepseries. I stopped on the lesson Vue, Laravel, and AJAXwith this error:

我开始了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 )

vue.js:2574 [Vue 警告]:避免直接改变 prop,因为每当父组件重新渲染时,该值将被覆盖。相反,根据道具的值使用数据或计算属性。正在变异的道具:“列表”(在组件中找到)

I have this code in main.js

我在main.js 中有这段代码

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

I know that the problem is in created()when I overwrite the list prop, but I am a newbie in Vue, so I totally don't know how to fix it. Anyone have an idea how (and please explain why) to fix it?

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

回答by ira

This has to do with the fact that mutating a prop locally is considered an anti-pattern in Vue 2

这与在 Vue 2 中本地改变 prop 被认为是反模式的事实有关

What you should do now, in case you want to mutate a prop locally, is to declare a field in your datathat uses the propsvalue as its initial value and then mutate the copy:

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

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

You can read more about this on Vue.js official guide

您可以在Vue.js 官方指南中阅读更多相关信息



Note 1:Please note that you should notuse the same name for your propand data, i.e.:

注1:请注意,您应该为你使用相同的名称propdata,即:

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

Note 2:Since I feel there is some confusionregarding propsand reactivity, I suggest you to have a look on thisthread

注2:由于我觉得关于props反应性有些混乱,我建议你看看这个线程

回答by chris

The Vue pattern is propsdown and eventsup. It sounds simple, but is easy to forget when writing a custom component.

Vue公司的模式是props下来events了。这听起来很简单,但在编写自定义组件时很容易忘记。

As of Vue 2.2.0 you can use v-model(with computed properties). I have found this combination creates a simple, clean, and consistent interface between components:

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

  • Any propspassed to your component remains reactive (i.e., it's not cloned nor does it require a watchfunction to update a local copy when changes are detected).
  • Changes are automatically emitted to the parent.
  • Can be used with multiple levels of components.
  • props传递给您的组件的任何内容都保持反应性(即,它不会被克隆,也不需要watch在检测到更改时更新本地副本的函数)。
  • 更改会自动发送到父级。
  • 可以与多个级别的组件一起使用。

A computed property permits the setter and getter to be separately defined. This allows the Taskcomponent to be rewritten as follows:

计算属性允许分别定义 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)
            }
        }
    }
})  

The modelproperty defines which propis associated with v-model, and which event will be emitted on changes. You can then call this component from the parent as follows:

模型,其属性定义prop相关联v-model,并且该事件将在改变发射。然后,您可以从父级调用此组件,如下所示:

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

The listLocalcomputed property provides a simple getter and setter interface within the component (think of it like being a private variable). Within #task-templateyou can render listLocaland it will remain reactive (i.e., if parentListchanges it will update the Taskcomponent). You can also mutate listLocalby calling the setter (e.g., this.listLocal = newList) and it will emit the change to the parent.

所述listLocal计算出的属性在组件内提供了一个简单的getter和setter接口(认为它像被一个私有变量)。在#task-template你可以渲染listLocal,它会保持反应性(即,如果parentList改变它会更新Task组件)。您还可以listLocal通过调用 setter(例如this.listLocal = newList)来改变它,它会将更改发送给父级。

What's great about this pattern is that you can pass listLocalto a child component of Task(using v-model), and changes from the child component will propagate to the top level component.

这种模式的优点在于您可以传递listLocalTask(using v-model) 的子组件,并且来自子组件的更改将传播到顶级组件。

For example, say we have a separate EditTaskcomponent for doing some type of modification to the task data. By using the same v-modeland computed properties pattern we can pass listLocalto the component (using v-model):

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

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

If EditTaskemits a change it will appropriately call set()on listLocaland thereby propagate the event to the top level. Similarly, the EditTaskcomponent could also call other child components (such as form elements) using v-model.

如果EditTask发出改变它会适当地调用set()listLocal,并由此传播事件到顶层。类似地,EditTask组件也可以使用 调用其他子组件(例如表单元素)v-model

回答by sealla

Vue just warns you: you change the prop in the component, but when parent component re-renders, "list" will be overwritten and you lose all your changes. So it is dangerous to do so.

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

Use computed property instead like this:

像这样使用计算属性:

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

回答by For the Name

If you're using Lodash, you can clone the prop before returning it. This pattern is helpful if you modify that prop on both the parent and child.

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

Let's say we have prop liston component grid.

假设我们在组件grid上有 prop列表

In Parent Component

在父组件中

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

In Child Component

在子组件中

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

回答by edison xue

Props down, events up. That's Vue's Pattern. The point is that if you try to mutate props passing from a parent. It won't work and it just gets overwritten repeatedly by the parent component. Child component can only emit an event to notify parent component to do sth. If you don't like these restrict, you can use VUEX(actually this pattern will suck in complex components structure, you should use VUEX!)

道具下降,事件上升。这就是 Vue 的模式。关键是,如果你试图改变从父级传递过来的 props。它不起作用,它只会被父组件反复覆盖。子组件只能发出一个事件来通知父组件做某事。如果你不喜欢这些限制,你可以使用 VUEX(实际上这种模式会吸收复杂的组件结构,你应该使用 VUEX!)

回答by Potato Running

You should not change the props's value in child component. If you really need to change it you can use .sync. Just like this

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

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

<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'
})

回答by burak buruk

According to the VueJs 2.0, you should not mutate a prop inside the component. They are only mutated by their parents. Therefore, you should define variables in data with different names and keep them updated by watching actual props. In case the list prop is changed by a parent, you can parse it and assign it to mutableList. Here is a complete solution.

根据 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);
        }
    }
});

It uses mutableList to render your template, thus you keep your list prop safe in the component.

它使用 mutableList 来呈现您的模板,因此您可以在组件中确保您的列表道具安全。

回答by Tango5614

do not change the props directly in components.if you need change it set a new property like this:

不要直接在组件中更改道具。如果您需要更改它,请设置一个新属性,如下所示:

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

And change the value of listClone.

并更改 listClone 的值。

回答by Vectrobyte

The answer is simple, you should break the direct prop mutation by assigning the value to some local component variables(could be data property, computed with getters, setters, or watchers).

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

Here's a simple solution using the watcher.

这是一个使用观察者的简单解决方案。

<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>

It's what I use to create any data input components and it works just fine. Any new data sent(v-model(ed)) from parent will be watched by the value watcher and is assigned to the input variable and once the input is received, we can catch that action and emit input to parent suggesting that data is input from the form element.

这是我用来创建任何数据输入组件的东西,它工作得很好。任何从父级发送(v-model(ed))的新数据都将被值观察者监视并分配给输入变量,一旦收到输入,我们就可以捕获该动作并向父级发出输入提示数据已输入从表单元素。

回答by ihsanberahim

I faced this issue as well. The warning gone after i use $onand $emit. It's something like use $onand $emitrecommended to sent data from child component to parent component.

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