web-dev-qa-db-fra.com

Vue 2 - Mutation des accessoires vue-warn

J'ai commencé https://laracasts.com/series/learning-vue-step-by-step series. Je me suis arrêté sur la leçon Vue, Laravel et AJAX avec cette erreur:

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

J'ai ce code dans main.js

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

Je sais que le problème réside dans created () lorsque j'écrase la propriété prop, mais je suis un débutant dans Vue, je ne sais donc absolument pas comment le résoudre. Quelqu'un a une idée comment (et s'il vous plaît expliquer pourquoi) comment le réparer?

100

Ceci est dû au fait que la mutation d'un accessoire localement est considéré comme un anti-motif dans Vue 2

Ce que vous devriez faire maintenant, au cas où vous voudriez transformer un prop localement}, est de déclarer un champ dans votre data qui utilise la valeur props comme valeur initiale, puis de muter la copie: 

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

Vous pouvez en lire plus à ce sujet sur Le guide officiel de Vue.js


Note 1: Veuillez noter que vous devriez pas utiliser le même nom pour votre prop et data, c'est-à-dire:

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

Note 2: Depuis je sens qu'il y a une certaine confusion en ce qui concerne props et réactivité, je vous suggère de jeter un coup d'œil sur this thread 

168
ira

Vue vous avertit simplement: vous modifiez l'accessoire dans le composant, mais lorsque le composant parent sera restitué, "list" sera écrasé et vous perdrez toutes vos modifications. C'est donc dangereux de le faire.

Utilisez la propriété calculée à la place comme ceci:

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

Props down, événements en place. C'est le motif de Vue. Le fait est que si vous essayez de muter un accessoire en passant de parent. Cela ne fonctionnera pas et sera simplement écrasé de manière répétée par le composant parent. Le composant enfant ne peut émettre un événement que pour informer le composant parent de le faire. Si vous n'aimez pas ces restrictions, vous pouvez utiliser VUEX (en fait, ce modèle aspire la structure de composants complexes, vous devriez utiliser VUEX!) 

17
edison xue

Si vous utilisez Lodash, vous pouvez cloner l'accessoire avant de le renvoyer. Ce modèle est utile si vous modifiez cet accessoire à la fois du parent et de l'enfant.

Disons que nous avons prop list sur le composant grid.

Dans la composante parentale

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

Dans la composante enfant

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

Le modèle Vue est simple: props down et events up. Cela semble simple, mais il est facile de l'oublier lors de l'écriture d'un composant personnalisé.

A partir de Vue 2.2.0, vous pouvez utiliser v-model (avec propriétés calculées ). Je trouve que cette combinaison crée une interface simple, propre et cohérente entre les composants:

  • Toute props transmise à votre composant reste réactive (c’est-à-dire qu’elle n’est pas clonée ni ne requiert une fonction watch pour mettre à jour une copie locale lorsque des modifications sont détectées).
  • Les modifications sont automatiquement émises au parent.
  • Peut être utilisé avec plusieurs niveaux de composants.

Une propriété calculée permet de définir séparément le getter et le getter. Cela permet au composant Task d'être réécrit comme suit:

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

La propriété model définit quelle prop est associée à v-model et quel événement sera émis lors des modifications. Vous pouvez ensuite appeler ce composant à partir du parent comme suit:

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

La propriété calculée listLocal fournit une interface getter/setter simple au sein du composant (pensez-y comme une variable privée). Dans #task-template, vous pouvez rendre listLocal et il restera réactif (c.-à-d. Si parentList change, le composant Task sera mis à jour). Vous pouvez également muter listLocal en appelant le setter (par exemple, this.listLocal = newList) et le changement sera transmis au parent.

Ce qui est génial avec ce modèle, c'est que vous pouvez passer listLocal à un composant enfant de Task (à l'aide de v-model), et les modifications apportées par le composant enfant seront propagées au composant de niveau supérieur.

Par exemple, supposons que nous ayons un composant EditTask distinct pour effectuer un certain type de modification des données de la tâche. En utilisant le même modèle v-model et les propriétés calculées, nous pouvons transmettre listLocal au composant (à l'aide de v-model):

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

Si EditTask émet une modification, il appelle de manière appropriée set() sur listLocal et propage ainsi l'événement au niveau supérieur. De même, le composant EditTask peut également appeler d'autres composants enfants (tels que des éléments de formulaire) à l'aide de v-model.

11
chris

Vous ne devez pas modifier la valeur des accessoires dans le composant enfant . Si vous devez réellement la modifier, vous pouvez utiliser .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
Potato Running

Selon VueJs 2.0, vous ne devez pas muter un accessoire dans le composant. Ils sont seulement mutés par leurs parents. Par conséquent, vous devez définir des variables dans les données avec des noms différents et les maintenir à jour en surveillant les accessoires réels . Si la propriété est modifiée par un parent, vous pouvez l'analyser et l'assigner à mutableList. Voici une solution complète.

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

Il utilise mutableList pour rendre votre modèle, vous gardez ainsi votre liste prop dans le composant.

6
burak buruk

ne changez pas les accessoires directement dans components.Si vous devez le changer, définissez une nouvelle propriété comme celle-ci:

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

Et changez la valeur de listClone.

5
Tango5614

J'ai également fait face à ce problème. L'avertissement a disparu après l'utilisation de $on et $emit. C'est un peu comme utiliser $on et $emit recommandé d'envoyer des données du composant enfant au composant parent.

4
ihsanberahim

flux de données unidirectionnel, .__ selon https://vuejs.org/v2/guide/components.html , le composant suit un sens... Flux de données, Tous les accessoires forment un liaison unidirectionnelle entre la propriété enfant et la propriété parent; lorsque la propriété parent est mise à jour, elle se répercute sur l'enfant, mais pas l'inverse, cela empêche les composants enfants de transformer accidentellement ceux du parent, ce qui peut rendre votre application flux de données plus difficile à comprendre.

De plus, chaque fois que le composant parent est mis à jour, tous les objets proposésdans les composants enfants sont actualisés avec la dernière valeur. Cela signifie que vous ne devez pas tenter de muter un accessoire dans un composant enfant. Si vous le faites, .vue vous avertira dans la console

Il existe généralement deux cas dans lesquels il est tentant de muter un accessoire: L’accessoire est utilisé pour transmettre une valeur initiale; le composant enfant souhaite l'utiliser par la suite en tant que propriété de données locale . L'accessoire est transmis en tant que valeur brute devant être transformée . La réponse appropriée à ces cas d'utilisation est la suivante: Définir une valeur locale propriété de données qui utilise la valeur initiale du prop comme valeur initiale:

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

Définissez une propriété calculée calculée à partir de la valeur de prop:

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

Si vous voulez muter les accessoires, utilisez object.

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

composant:

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

Vous devez ajouter une méthode calculée comme ceci 

composant.vue

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

Ajoutant à la meilleure réponse,

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

Le paramétrage des accessoires par un tableau est destiné au développement/prototypage. En production, assurez-vous de définir les types d’accessoires ( https://vuejs.org/v2/guide/components-props.html ) et définissez une valeur par défaut dans le cas contraire. l'accessoire n'a pas été peuplé par le parent, comme tel.

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

De cette façon, vous obtenez au moins un objet vide dans mutableList au lieu d’une erreur JSON.parse s’il n’est pas défini.

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

Peut-être que cela répondra à vos besoins.

2
D.P

Les accessoires de Vue.js ne doivent pas être mutés car ceci est considéré comme un anti-modèle dans Vue.

L'approche que vous devez adopter consiste à créer une propriété de données sur votre composant qui référence la propriété originale prop de list.

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

Maintenant, votre structure de liste transmise au composant est référencée et mutée via la propriété data de votre composant :-)

Si vous souhaitez faire plus que simplement analyser votre propriété list, utilisez alors la propriété computed du composant Vue .. Cela vous permet de faire des mutations plus en profondeur de vos accessoires.

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

L'exemple ci-dessus analyse votre list prop et le filtre en seulement actif list-tems, le déconnecte pour les commandes et les rires et le renvoie.

note: les deux propriétés data & computed sont référencées dans le modèle de la même manière, par exemple 

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

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

Il peut être facile de penser qu’une propriété computed (étant une méthode) doit être appelée ... elle ne le fait pas.

2
Francis Leigh

Vue.js considère cela comme un anti-motif. Par exemple, en déclarant et en définissant des accessoires comme

this.propsVal = 'new Props Value'

Donc, pour résoudre ce problème, vous devez prendre une valeur allant des accessoires aux données ou à la propriété calculée d'une instance de Vue, comme ceci:

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

Cela fonctionnera certainement.

0
iamsangeeth