Vue 2 – Les accessoires mutants vue-warn

J’ai commencé la série https://laracasts.com/series/learning-vue-step-by-step . 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 ) 

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 est dans created () quand je remplace le prop de la liste, mais je suis un débutant dans Vue, donc je ne sais pas du tout comment le réparer. Quelqu’un a une idée de comment (et s’il vous plaît expliquer pourquoi) pour y remédier?

Cela a à voir avec le fait que la mutation locale d’un prop est considérée comme un anti-pattern dans Vue 2 (voir ici )

Ce que vous devez faire maintenant, au cas où vous souhaiteriez muter un accessoire localement , est de déclarer un champ dans vos data qui utilise la valeur des 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); } } }); 

S’il vous plaît voir aussi ce numéro ici

J’espère que cela t’aides


Note 1: Tenez compte du fait que, selon cet article , vous ne devriez pas essayer d’utiliser le même nom pour vos prop et data , c’est-à-dire:

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

Note 2: Comme j’estime qu’il y a une certaine confusion en ce qui concerne les props et la réactivité , je vous suggère de jeter un coup d’oeil sur ce sujet.

Vue vous avertit: vous changez le prop dans le composant, mais lorsque le composant parent est rendu, “list” sera écrasé et vous perdrez toutes vos modifications. Il est donc dangereux de le faire.

Utilisez la propriété calculée insted comme ceci:

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

Si vous utilisez Lodash, vous pouvez cloner le prop avant de le retourner. Ce modèle est utile si vous modifiez cet accessoire sur le parent et l’enfant.

Disons que nous avons une liste de props sur la grid des composants.

Composant parent

  

Dans la composante enfant

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

Props down, événements en haut. C’est le motif de la vue. Le fait est que si vous essayez de muter un accessoire en passant de parent. Cela ne fonctionnera pas et il sera simplement écrasé à plusieurs resockets par le composant parent. Le composant enfant peut uniquement émettre un événement pour notifier le composant parent sth. Si vous n’aimez pas ces ressortingctions, vous pouvez utiliser VUEX (en fait, ce modèle va aspirer la structure de composants complexes, vous devez utiliser VUEX!)

Vous ne devez pas modifier la valeur des éléments dans le composant enfant. Si vous avez vraiment besoin de le changer, vous pouvez utiliser .sync . Juste comme ça

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

J’ai également fait face à ce problème. L’avertissement est parti après que j’utilise $on et $emit . C’est quelque chose comme use $on et $emit recommandé pour envoyer des données du composant enfant au composant parent.

Selon le VueJs 2.0, vous ne devez pas modifier un accessoire dans le composant. Ils ne sont mutés que par leurs parents. Par conséquent, vous devez définir des variables dans des données avec des noms différents et les tenir à jour en regardant les accessoires réels. Si le parent de la liste est modifié, vous pouvez l’parsingr et l’assigner à mutableList. Voici une solution complète.

 Vue.component('task', { template: ´
  • {{item.name}}
´, 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, ainsi vous conservez votre liste prop dans le composant.

Si vous voulez muter des accessoires, utilisez un object.

  

composant:

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

Vous devez append la méthode calculée comme ceci

composant.vue

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

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

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

  • Tout props transmis à votre composant rest réactif (c’est-à-dire qu’il n’est pas cloné et qu’il n’a pas besoin de fonction de watch pour mettre à jour une copie locale lorsque des modifications sont détectées).
  • Les modifications sont automatiquement émises vers le parent.
  • Peut être utilisé avec plusieurs niveaux de composants.

Une propriété calculée permet de définir séparément le setter et le getter. Cela permet de réécrire le composant Task 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 quel prop est associé à v-model et quel événement sera émis lors des modifications. Vous pouvez ensuite appeler ce composant à partir du parent comme suit:

  

La propriété calculée listLocal fournit une interface getter et setter simple dans le composant (pensez à une variable privée). Dans #task-template vous pouvez rendre listLocal et il restra réactif (par exemple, si parentList change, il mettra à jour le composant Task ). Vous pouvez également muter listLocal en appelant le setter (par exemple, this.listLocal = newList ) et il émettra la modification pour le parent.

Ce qui est vraiment génial avec ce modèle, c’est que vous pouvez transmettre listLocal à un composant enfant de Task (en utilisant v-model ), et les modifications du composant enfant se propageront au composant de niveau supérieur.

Par exemple, disons que nous avons un composant EditTask distinct pour effectuer un certain type de modification des données de la tâche. En utilisant le même v-model et de propriétés calculées, nous pouvons passer listLocal au composant (en utilisant v-model ):

  

Si EditTask émet un changement, il appelle correctement 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 .

un stream de données à sens unique, selon https://vuejs.org/v2/guide/components.html , le composant suit un stream de données à sens unique, tous les accessoires forment une liaison à sens unique entre la propriété enfant et le parent Premièrement, lorsque la propriété parent est mise à jour, elle est transmise à l’enfant, mais pas l’inverse. Cela empêche les composants enfants de muter accidentellement ceux du parent, ce qui peut rendre le stream de données de votre application plus difficile à comprendre.

De plus, chaque fois que le composant parent est mis à jour, tous les accessoires des 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 y a généralement deux cas où il est tentant de muter un accessoire: le prop est utilisé pour passer une valeur initiale; le composant enfant veut l’utiliser ensuite comme une propriété de données locale. Le prop est transmis comme une valeur brute qui doit être transformée. La réponse appropriée à ces cas d’utilisation est la suivante: Définissez une propriété de données locale 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 du prop:

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

ne changez pas les accessoires directement dans components.si vous avez besoin de les modifier, définissez une nouvelle propriété comme ceci:

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

Et changez la valeur de listClone.

Les accessoires Vue.js ne doivent pas être mutés car cela est considéré comme un anti-pattern dans Vue.

L’approche à suivre consiste à créer une propriété de données sur votre composant qui référence la propriété prop d’ origine de la liste.

 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 parsingr votre propriété de liste, utilisez computed propriété computed composant Vue. Cela vous permet de faire des mutations plus profondes sur 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 parsing votre prop de liste et le filtre uniquement aux systèmes de liste actifs , le déconnecte pour des schnits et des rires et le renvoie.

note : les data et computed propriétés computed sont référencées dans le modèle, par exemple

 
{{parsedList}}
{{filteredJSONList}}

Il peut être facile de penser qu’une propriété computed (en tant que méthode) doit être appelée … elle ne l’est pas

Vue.js considère cela comme un anti-pattern. Par exemple, déclarer et définir des accessoires comme

 this.propsVal = 'new Props Value' 

Donc, pour résoudre ce problème, vous devez intégrer une valeur des propriétés 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.

En savoir plus, est ce que vous voulez utiliser un filtre pour la liste des composants. déclara unavaviable buscar de forma local un composant et un metodo calculés pour usa la lista y filtrarla ..

 Vue.component('comun-list',{ props: ['list'], data: function () { return { buscar: '', } }, template: `
  • {{item.title}}
`, computed: { buscarList: function(){ return this.list.filter((item) => item.title.includes(this.buscar)); }, }, });