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?
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
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);
}
}
});
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!)
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)
}
}
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:
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).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.
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'
})
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.
Ä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.
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.
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()
}
}
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";
}
}
Sie müssen eine berechnete Methode wie folgt hinzufügen
component.vue
props: ['list'],
computed: {
listJson: function(){
return JSON.parse(this.list);
}
}
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.
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.
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
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>
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.