wake-up-neo.com

Vue 2 - Mutierende Requisiten vue-warn

Ich habe https://laracasts.com/series/learning-vue-step-by-step serie gestartet. Ich unterbrach die Lektion Vue, Laravel und AJAX mit diesem Fehler:

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

Ich habe diesen Code in main.js

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

Ich weiß, dass das Problem in created () liegt, wenn ich die Listenreferenz überschreibe, aber ich bin ein Neuling in Vue, daher weiß ich nicht, wie ich es beheben kann. Hat jemand eine Idee, wie (und bitte erklärt warum) das Problem zu beheben ist?

100

Dies hat mit der Tatsache zu tun, dass das lokale Mutieren einer Requisite als Anti-Muster in Vue 2 gilt.

Was Sie jetzt tun sollten, um eine Requisite lokal zu mutieren , ist, ein Feld in Ihrem data zu deklarieren, das das props verwendet. Wert als Anfangswert und dann die Kopie mutieren:

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

Sie können mehr darüber lesen auf Vue.js offizieller Leitfaden


Anmerkung 1: Bitte beachten Sie, dass Sie sollten nicht Verwenden Sie den gleichen Namen für Ihre prop und data, dh:

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

Anmerkung 2: Da ich glaube, es gibt einige Verwirrung in Bezug auf props und Reaktivität , ich schlage vor, dass Sie einen Blick auf this Thread werfen

204
ira

Vue warnt Sie nur: Sie ändern die Requisite in der Komponente, aber wenn die übergeordnete Komponente erneut gerendert wird, wird "list" überschrieben und Sie verlieren alle Ihre Änderungen. Es ist also gefährlich, dies zu tun.

Verwenden Sie stattdessen die berechnete Eigenschaft:

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

Props down, Ereignisse auf. Das ist Vues Muster. Der Punkt ist der, wenn Sie versuchen, Requisiten zu mutieren, die von einem Elternteil übergeben werden. Es funktioniert nicht und wird einfach wiederholt von der übergeordneten Komponente überschrieben. Die untergeordnete Komponente kann nur ein Ereignis ausgeben, um die übergeordnete Komponente zu benachrichtigen, dass sie etw tun soll. Wenn Sie diese Einschränkungen nicht mögen, können Sie VUEX verwenden (dieses Muster saugt tatsächlich komplexe Komponentenstrukturen an, Sie sollten VUEX verwenden!) 

17
edison xue

Wenn Sie Lodash verwenden, können Sie die Requisite klonen, bevor Sie sie zurückschicken. Dieses Muster ist hilfreich, wenn Sie diese Requisite sowohl für das übergeordnete Element als auch für das untergeordnete Element ändern.

Nehmen wir an, wir haben prop list für Komponente grid.

In übergeordneter Komponente

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

In einer untergeordneten Komponente

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

Das Vue-Muster ist einfach: props down und events up. Das hört sich einfach an, ist jedoch beim Schreiben einer benutzerdefinierten Komponente leicht zu vergessen.

Ab Vue 2.2.0 können Sie v-model (mit berechneten Eigenschaften ) verwenden. Ich finde, diese Kombination schafft eine einfache, saubere und konsistente Schnittstelle zwischen Komponenten:

  • Jede props, die an Ihre Komponente übergeben wird, bleibt reaktiv (d. H. Sie wird weder geklont, noch benötigt sie eine watch-Funktion, um eine lokale Kopie zu aktualisieren, wenn Änderungen festgestellt werden).
  • Änderungen werden automatisch an das übergeordnete Element gesendet.
  • Kann mit mehreren Komponentenebenen verwendet werden.

Eine berechnete Eigenschaft ermöglicht die getrennte Definition von Setter und Getter. Dadurch kann die Task-Komponente wie folgt umgeschrieben werden:

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

Die Eigenschaft model definiert, welche prop mit v-model verknüpft ist und welches Ereignis bei Änderungen ausgegeben wird. Sie können diese Komponente dann wie folgt vom übergeordneten Element aus aufrufen:

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

Die berechnete Eigenschaft listLocal bietet eine einfache Getter- und Setter-Schnittstelle in der Komponente (man kann sich das wie eine private Variable vorstellen). Innerhalb von #task-template können Sie listLocal rendern und es bleibt reaktiv (d. H., Wenn parentList geändert wird, wird die Task-Komponente aktualisiert). Sie können listLocal auch durch Aufruf des Setters (z. B. this.listLocal = newList) mutieren, und die Änderung wird an das übergeordnete Element gesendet.

Das Beste an diesem Muster ist, dass Sie listLocal an eine untergeordnete Komponente von Task übergeben können (mithilfe von v-model), und Änderungen von der untergeordneten Komponente werden an die Komponente der obersten Ebene weitergegeben.

Angenommen, wir verfügen über eine separate EditTask-Komponente, um Änderungen an den Aufgabendaten vorzunehmen. Mit dem gleichen v-model und dem berechneten Eigenschaftsmuster können wir listLocal an die Komponente übergeben (mit v-model):

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

Wenn EditTask eine Änderung auslöst, ruft es set() für listLocal auf und leitet das Ereignis an die oberste Ebene weiter. In ähnlicher Weise könnte die EditTask-Komponente auch andere untergeordnete Komponenten (z. B. Formularelemente) mit v-model aufrufen.

11
chris

Sie sollten den Wert der Requisiten in der untergeordneten Komponente nicht ändern. __ Wenn Sie ihn wirklich ändern müssen, können Sie .sync..__ verwenden. Einfach so

<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

Laut VueJs 2.0 sollten Sie keine Requisite innerhalb der Komponente mutieren. Sie werden nur von ihren Eltern mutiert. Daher sollten Sie Variablen in Daten mit unterschiedlichen Namen definieren und diese auf dem neuesten Stand halten, indem Sie die aktuellen Requisiten betrachten. Falls die Listenreferenz von einem übergeordneten Element geändert wird, können Sie sie parsen und mutableList zuweisen. Hier ist eine Komplettlösung.

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

Es verwendet mutableList, um Ihre Vorlage zu rendern, sodass Sie Ihre Listenoptik in der Komponente sicher aufbewahren.

6
burak buruk

Ändern Sie die Requisiten nicht direkt in components.Wenn Sie dies ändern müssen, setzen Sie eine neue Eigenschaft wie folgt:

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

Und ändern Sie den Wert von ListClone.

5
Tango5614

Ich bin auch mit diesem Problem konfrontiert. Die Warnung verschwindet, nachdem ich $on und $emit..__ verwendet habe. Es ist so etwas wie die Verwendung von $on und $emit, um Daten von der untergeordneten Komponente zur übergeordneten Komponente zu senden.

4
ihsanberahim

datenfluss in einer Richtung, gemäß https://vuejs.org/v2/guide/components.html , die Komponente folgt in einer Richtung Datenfluss, Alle Requisiten bilden a One-Down-Down-Bindung zwischen der untergeordneten Eigenschaft und der übergeordneten Eigenschaft. Wenn die übergeordnete Eigenschaft aktualisiert wird, wird sie an das untergeordnete Objekt weitergegeben, nicht umgekehrt. Dies verhindert, dass untergeordnete Komponenten versehentlich das übergeordnete Element ändern, was zu einer App führen kann Datenfluss schwerer zu verstehen.

Jedes Mal, wenn die übergeordnete Komponente aktualisiert wird, werden alle propsin der untergeordneten Komponenten mit dem neuesten Wert aktualisiert. Dies bedeutet, dass Sie nicht versuchen sollten, eine Requisite in einer untergeordneten Komponente zu mutieren. Wenn Sie dies tun, werden Sie in der Konsole gewarnt.

In der Regel gibt es zwei Fälle, in denen versucht wird, eine Requisite zu verändern: .__ Die Requisite wird verwendet, um einen Anfangswert zu übergeben. Die untergeordnete Komponente möchte sie anschließend als lokale Dateneigenschaft verwenden. Die Requisite wird als Rohwert übergeben, der umgewandelt werden muss. Die richtige Antwort auf diese Anwendungsfälle lautet: Definieren Sie eine lokale Dateneigenschaft, die den Anfangswert der Stütze als Anfangswert verwendet:

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

Definieren Sie eine berechnete Eigenschaft, die aus dem Wert der Eigenschaft berechnet wird:

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

Wenn Sie Requisiten mutieren möchten - verwenden Sie das Objekt.

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

komponente:

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

Sie müssen eine berechnete Methode wie folgt hinzufügen 

component.vue

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

Die beste Antwort hinzufügen,

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

Das Festlegen von Requisiten durch ein Array ist für das Entwickeln von Prototypen gedacht. Stellen Sie in der Produktion sicher, dass Sie die Stiltypen ( https://vuejs.org/v2/guide/components-props.html ) und einen Standardwert für den Fall festlegen Die Requisite wurde nicht von der Muttergesellschaft so besetzt.

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

Auf diese Weise erhalten Sie zumindest ein leeres Objekt in mutableList anstelle eines JSON.parse-Fehlers, wenn es nicht definiert ist.

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

Vielleicht entspricht dies Ihren Bedürfnissen.

2
D.P

Vue.js Requisiten dürfen nicht mutiert werden, da dies als Anti-Pattern in Vue betrachtet wird.

Sie müssen eine Dateneigenschaft für Ihre Komponente erstellen, die auf die ursprüngliche Eigenschaft prop von list verweist.

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

Nun wird Ihre Listenstruktur, die an die Komponente übergeben wird, über die data-Eigenschaft Ihrer Komponente referenziert und mutiert :-)

Wenn Sie mehr als nur Ihre Listeneigenschaft analysieren möchten, verwenden Sie die Vue-Komponente 'computed-Eigenschaft .. _. Dadurch können Sie tiefergehende Mutationen an Ihren Requisiten vornehmen.

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

Das obige Beispiel parst Ihre list - Eigenschaft und filtert sie auf nur active list-tems ab, meldet sie ab für Schnitts und Kichern und gibt sie zurück.

note: Beide data- und computed -Eigenschaften werden in der Vorlage auf dieselbe Weise referenziert, z 

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

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

Es ist leicht zu glauben, dass eine computed-Eigenschaft (eine Methode) aufgerufen werden muss ... es ist nicht der Fall

2
Francis Leigh

Zusätzlich zu den oben genannten für andere mit dem folgenden Problem:

"Wenn der Wert der Requisiten nicht erforderlich ist und daher nicht immer zurückgegeben wird, geben die übergebenen Daten undefined zurück (anstatt leer)." Was <select> Standardwert vermasseln könnte, löste ich, indem ich überprüfte, ob der Wert in beforeMount() eingestellt ist (und wenn nicht), wie folgt:

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>
0
Maroun Melhem

Vue.js betrachtet dies als Anti-Muster. Zum Beispiel das Deklarieren und Setzen einiger Requisiten wie

this.propsVal = 'new Props Value'

Um dieses Problem zu lösen, müssen Sie einen Wert aus den Requisiten in die Daten oder die berechnete Eigenschaft einer Vue-Instanz wie folgt übernehmen:

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

Das wird definitiv funktionieren.

0
iamsangeeth