Was ist der richtige Weg, um Requisiten als Anfangsdaten in Vue.js 2 zu übergeben?

97

Ich möchte also Requisiten an eine Vue-Komponente übergeben, aber ich erwarte, dass sich diese Requisiten in Zukunft innerhalb dieser Komponente ändern, z. B. wenn ich diese Vue-Komponente mithilfe von AJAX von innen aktualisiere. Sie dienen also nur zur Initialisierung der Komponente.

Mein cars-listVue-Komponentenelement, an das ich Requisiten mit anfänglichen Eigenschaften übergebe single-car:

// cars-list.vue

<script>
    export default {
        data: function() {
            return {
                cars: [
                    {
                        color: 'red',
                        maxSpeed: 200,
                    },
                    {
                        color: 'blue',
                        maxSpeed: 195,
                    },
                ]
            }
        },
    }
</script>

<template>
    <div>
        <template v-for="car in cars">
            <single-car :initial-properties="car"></single-car>
        </template>
    </div>
</template>

So wie ich es jetzt single-carmache, ordne ich es in meiner Komponente this.initialPropertiesmeinem this.data.propertiesOn- created()Initialisierungs-Hook zu. Und es funktioniert und ist reaktiv.

// single-car.vue

<script>
    export default {
        data: function() {
            return {
                properties: {},
            }
        },
        created: function(){
            this.data.properties = this.initialProperties;
        },
    }
</script>

<template>
    <div>Car is in {{properties.color}} and has a max speed of {{properties.maxSpeed}}</div>
</template>

Aber mein Problem dabei ist, dass ich nicht weiß, ob das der richtige Weg ist? Wird es mir nicht Probleme auf der Straße bereiten? Oder gibt es einen besseren Weg, dies zu tun?

Dominik Serafin
quelle
13
Dies ist die verwirrende Sache über Vue meiner Meinung nach : Jeder dataist two-wayverpflichtet, aber man kann nicht passieren , dataum Komponenten, Sie passieren props, aber man kann nicht die empfangene ändern propsnoch wandeln das propszu data. Dann was? Eine Sache, die ich gelernt habe, ist, dass Sie propsEreignisse weitergeben und auslösen sollten . Das heißt, wenn die Komponente den propsEmpfang ändern möchte , sollte sie ein Ereignis aufrufen und "neu gerendert" werden. Aber dann bleibt dir eine one-wayBindung genau wie bei React und ich sehe die Verwendung für datadann nicht. Ziemlich verwirrend.
André Pena
1
Daten sind in erster Linie für den privaten Gebrauch der Komponente bestimmt. Alles, was im Kontext der Komponente darauf platziert wird, ist reaktiv und kann gebunden werden. Das Konzept mit Requisiten besteht darin, Werte an eine Komponente zu übergeben, die Komponente jedoch daran zu hindern, Statusänderungen im übergeordneten Element stillschweigend einzuführen, indem ein übergebener Wert geändert wird. Es ist besser, es in einem von Ihnen angegebenen Ereignis explizit zu machen. Dies war eine Änderung der Philosophie von Vue 1.0 auf 2.0.
David K. Hess
Heute habe ich versucht, hier einen Thread zu starten: forum.vuejs.org/t/…
bgraves

Antworten:

101

Dank dieser https://github.com/vuejs/vuejs.org/pull/567 kenne ich die Antwort jetzt.

Methode 1

Übergeben Sie die erste Requisite direkt an die Daten. Wie das Beispiel in aktualisierten Dokumenten:

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

Beachten Sie jedoch, dass jede Änderung an dieser Requisite zu einer Änderung des Status der übergeordneten Komponente führt, wenn die übergebene Requisite ein Objekt oder Array ist, das im Status der übergeordneten Komponente verwendet wird.

Warnung : Diese Methode wird nicht empfohlen. Dadurch werden Ihre Komponenten unvorhersehbar. Wenn Sie übergeordnete Daten aus untergeordneten Komponenten festlegen müssen, verwenden Sie entweder die Statusverwaltung wie Vuex oder "v-model". https://vuejs.org/v2/guide/components.html#Using-v-model-on-Components

Methode 2

Wenn Ihre ursprüngliche Requisite ein Objekt oder Array ist und Sie nicht möchten, dass Änderungen im untergeordneten Status in den übergeordneten Status übertragen werden, verwenden Sie einfach z. B. Vue.util.extend[1], um eine Kopie der Requisiten zu erstellen, anstatt sie direkt auf untergeordnete Daten zu verweisen, wie folgt:

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

Methode 3

Sie können auch den Spread-Operator verwenden, um die Requisiten zu klonen. Weitere Details in der Igor-Antwort: https://stackoverflow.com/a/51911118/3143704

Beachten Sie jedoch, dass Spread-Operatoren in älteren Browsern nicht unterstützt werden. Für eine bessere Kompatibilität müssen Sie den Code z babel. B. mithilfe von transpilieren .

Fußnoten

[1] Beachten Sie, dass dies ein internes Vue-Dienstprogramm ist und sich mit neuen Versionen ändern kann. Möglicherweise möchten Sie andere Methoden verwenden, um diese Requisite zu kopieren. Weitere Informationen finden Sie unter " Wie klone ich ein JavaScript-Objekt korrekt? ".

Meine Geige, wo ich sie getestet habe: https://jsfiddle.net/sm4kx7p9/3/

Dominik Serafin
quelle
1
Ist es also in Ordnung, Änderungen an die übergeordnete Komponente weiterzugeben?
igor
1
@igor persönlich Ich würde versuchen, es so weit wie möglich zu vermeiden. Dadurch werden Ihre Komponenten unvorhersehbar. Ich denke, ein besserer Weg, um Änderungen an der übergeordneten Komponente zu erhalten, ist die Verwendung der "v-model" -Methode, bei der Sie Änderungen von Ihrer untergeordneten Komponente an die übergeordnete Komponente ausgeben. vuejs.org/v2/guide/components.html#Using-v-model-on-Components
Dominik Serafin
ja aber was ist mit tiefen Objekten? soll ich tief kopieren? sollte ich meine Komponenten so umgestalten, dass keine tiefen Objekte verwendet werden? dh eine Requisite mit: { ok: 1, notOk: { meh: 2 } }Wenn auf interne Daten mit einer der vorherigen Klonierungsmethoden gesetzt, würde die Requisite immer noch geändertnotOk.meh
Nikso
1
Vielen Dank für die hervorragende Frage und Antwort @DominikSerafin. Dies zeigt perfekt die Achillesheilung von Vue.js. Als Framework handhabt es die Komponentisierung so gut, aber das Übergeben von Daten zwischen den Comps ist eine wichtige PITA. Schauen Sie sich einfach den Nomenklatur-Albtraum an. Jetzt muss ich allen meinen Requisiten ein Präfix voranstellen initial, um sie in meinem Comp verwenden zu können. Es wäre viel sauberer, wenn wir einfach eine Requisite dataan einen beliebigen Comp weitergeben und die lokalisierten Daten ohne all diesen initialPropNameWahnsinn automatisch weitergeben könnten .
AJB
1
@ DominikSerafin Ich höre, was du sagst, und du liegst nicht falsch. Aber schreiben Sie eine Form-Builder-App mit Vue.js und Sie werden schnell sehen, was ich meine. Ich bin gerade dabei, meine Technik des Datenaustauschs von prop --> comp.datader Verwendung von Vue-Stash für alle Daten zu ändern (oder Vuex würde auch funktionieren). Aber jetzt pralle ich meine Daten einfach windowwie früher vom Objekt ab, bevor die "modernen" JS-Frameworks mir ihre Meinung auferlegten. Es stellt sich also die Frage: Wozu dient die Eigenschaft comp.data überhaupt?
AJB
24

In Begleitung der Antwort von @ dominik-serafin:

Wenn Sie ein Objekt übergeben, können Sie es einfach mit dem Spread-Operator (ES6-Syntax) klonen:

 props: {
   record: {
     type: Object,
     required: true
   }
 },

data () { // opt. 1
  return {
    recordLocal: {...this.record}
  }
},

computed: { // opt. 2
  recordLocal () {
    return {...this.record}
  }
},

Am wichtigsten ist es jedoch, sich daran zu erinnern, opt zu verwenden. 2 für den Fall, dass Sie einen berechneten Wert oder mehr einen asynchronen Wert übergeben. Andernfalls wird der lokale Wert nicht aktualisiert.

Demo:

Vue.component('card', { 
  template: '#app2',
  props: {
    test1: null,
    test2: null
  },
  data () { // opt. 1
    return {
      test1AsData: {...this.test1}
    }
  },
  computed: { // opt. 2
    test2AsComputed () {
      return {...this.test2}
    }
  }
})

new Vue({
  el: "#app1",
  data () {
    return {
      test1: {1: 'will not update'},
      test2: {2: 'will update after 1 second'}
    }
  },
  mounted () {
    setTimeout(() => {
      this.test1 = {1: 'updated!'}
      this.test2 = {2: 'updated!'}
    }, 1000)
  }
})
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>

<div id="app1">
  <card :test1="test1" :test2="test2"></card>
</div>

<template id="app2">
  <div>
    test1 as data: {{test1AsData}}
    <hr />
    test2 as computed: {{test2AsComputed}}
  </div>
</template>

https://jsfiddle.net/nomikos3/eywraw8t/281070/

Igor Parra
quelle
Hey @ igor-parra, anscheinend haben Sie doppelte Antworten eingereicht. Außerdem sollte erwähnt werden, dass der Spread-Operator nicht in allen Browsern unterstützt wird und für eine bessere Kompatibilität möglicherweise einen Transpiling-Schritt benötigt. Ansonsten sieht es gut aus - danke, dass Sie diese alternative Methode hinzugefügt haben!
Dominik Serafin
@ dominik-serafin Ja, Sie haben Recht, ich habe einen Verweis auf ES6 hinzugefügt. In Webpack-basierten Anwendungen ist es so einfach, Babel vorkonfiguriert zu haben, um das moderne Javascript voll auszunutzen.
Igor Parra
recordLocal: [...this.record](in meinem Fall ist der Datensatz ein Array) hat bei mir nicht funktioniert - nach dem Laden und Überprüfen der vue-Komponente, recordenthält die Elemente und recordLocalwar ein leeres Array.
Christian
Wenn ich es über die COMPUTED-Eigenschaft verwende, funktioniert es gut. Wenn ich versuche, die Daten computed
festzulegen, funktioniert
Sei vorsichtig. Der Spread-Operator klont nur flach, sodass Sie für Objekte, die Objekte oder Arrays enthalten, weiterhin Zeiger kopieren, anstatt eine neue Kopie zu erhalten. Ich benutze cloneDeep von lodash.
Cindy Conway
13

Ich glaube, Sie machen es richtig, weil es das ist, was in den Dokumenten angegeben ist.

Definieren Sie eine lokale Dateneigenschaft, die den Anfangswert der Requisite als Anfangswert verwendet

https://vuejs.org/guide/components.html#One-Way-Data-Flow

jonan.pineda
quelle
4
Für Pass-by-Value-Requisiten ist das in Ordnung. In diesem Fall handelt es sich um ein Objekt. Wenn Sie es ändern, wirkt sich dies auf das übergeordnete Objekt aus. Auf dieser Seite: "Beachten Sie, dass Objekte und Arrays in JavaScript als Referenz übergeben werden. Wenn es sich bei der Requisite also um ein Array oder Objekt handelt, wirkt sich das Mutieren des Objekts oder Arrays selbst innerhalb der untergeordneten Komponente auf den übergeordneten Status aus."
Jake
Das funktioniert bei mir und ist klar dokumentiert. Diese Zeile in der obigen Antwort steht im Widerspruch zur Vue-Dokumentation, soweit ich sagen kann: "Warnung: Diese Methode wird nicht empfohlen. Dadurch werden Ihre Komponenten unvorhersehbar. Wenn Sie übergeordnete Daten von untergeordneten Komponenten festlegen müssen, verwenden Sie entweder die Statusverwaltung wie Vuex oder verwenden Sie "V-Modell". "
Nathaniel Rink
Nach 4 Jahren und Hunderten von Stunden Lernkurve bin ich wieder da, wo ich angefangen habe, Vars vom windowObjekt abzuprallen .
AJB