Qual è il modo corretto per passare gli oggetti di scena come dati iniziali in Vue.js 2?


97

Quindi voglio passare gli oggetti di scena a un componente Vue, ma mi aspetto che questi oggetti di scena cambino in futuro dall'interno di quel componente, ad esempio quando aggiorno quel componente Vue dall'interno usando AJAX. Quindi sono solo per l'inizializzazione del componente.

Il mio cars-listelemento componente Vue in cui passo gli oggetti di scena con proprietà iniziali a 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>

Il modo in cui lo faccio in questo momento che dentro la mia single-carcomponente sto assegnando this.initialPropertiesalla mia this.data.propertiessul created()gancio di inizializzazione. E funziona ed è reattivo.

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

Ma il mio problema è che non so se è un modo corretto per farlo? Non mi causerà dei guai lungo la strada? O c'è un modo migliore per farlo?


13
Questa è la cosa più confusa di Vue secondo me: ogni dataè two-wayvincolato, ma non puoi passare dataai componenti, passi props, ma non puoi cambiare il ricevuto propsné convertirlo propsin data. Allora cosa? Una cosa che ho imparato è che dovresti tramandare propse innescare eventi. In altre propsparole , se il componente desidera modificare la ricezione, deve chiamare un evento ed essere "sottoposto a rendering". Ma poi ti rimane un one-waybinding esattamente come React e non vedo l'uso per dataallora. Abbastanza confuso.
André Pena

1
I dati sono destinati principalmente all'uso privato del componente. Tutto ciò che viene posto su di esso nel contesto del componente è reattivo e può essere vincolato. Il concetto con gli oggetti di scena è di passare i valori in un componente, ma impedire al componente di essere in grado di introdurre silenziosamente cambiamenti di stato nel genitore modificando un valore passato. È meglio renderlo esplicito in un evento come hai indicato. Questo è stato un cambiamento di filosofia da Vue 1.0 a 2.0.
David K. Hess

Oggi ho provato ad avviare un thread qui: forum.vuejs.org/t/…
bgraves

Risposte:


103

Grazie a questo https://github.com/vuejs/vuejs.org/pull/567 ora conosco la risposta.

Metodo 1

Passa il prop iniziale direttamente ai dati. Come l'esempio nei documenti aggiornati:

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

Ma tieni a mente se il prop passato è un oggetto o un array che viene utilizzato nello stato del componente genitore, qualsiasi modifica a quel prop risulterà nel cambiamento in quello stato del componente genitore.

Attenzione : questo metodo non è consigliato. Renderà imprevedibili i tuoi componenti. Se è necessario impostare i dati padre dai componenti figlio, utilizzare la gestione dello stato come Vuex o utilizzare "v-model". https://vuejs.org/v2/guide/components.html#Using-v-model-on-Components

Metodo 2

Se il tuo oggetto Vue.util.extenddi scena iniziale è un oggetto o un array e se non vuoi che i cambiamenti nello stato dei figli si propagino allo stato genitore, usa ad esempio [1] per fare una copia degli oggetti invece che puntano direttamente ai dati dei bambini, come questo:

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

Metodo 3

È inoltre possibile utilizzare l'operatore di diffusione per clonare gli oggetti di scena. Maggiori dettagli nella risposta di Igor: https://stackoverflow.com/a/51911118/3143704

Ma tieni presente che gli operatori di diffusione non sono supportati nei browser meno recenti e per una migliore compatibilità dovrai trasferire il codice, ad esempio usando babel .

Note a piè di pagina

[1] Tieni presente che si tratta di un'utilità Vue interna e potrebbe cambiare con le nuove versioni. Potresti voler usare altri metodi per copiare quell'oggetto, vedi " Come faccio a clonare correttamente un oggetto JavaScript? ".

Il mio violino dove lo stavo provando: https://jsfiddle.net/sm4kx7p9/3/


1
quindi è normale che le modifiche vengano propagate al componente genitore?
igor

1
@igor personalmente proverei ad evitarlo il più possibile. Renderà imprevedibili i tuoi componenti. Penso che il modo migliore per ottenere modifiche nel componente genitore sia usare il metodo "v-model" in cui $ emetti le modifiche dal tuo componente figlio al componente genitore. vuejs.org/v2/guide/components.html#Using-v-model-on-Components
Dominik Serafin

si ma per quanto riguarda gli oggetti profondi? devo copiare in profondità? devo riprogettare i miei componenti per non utilizzare oggetti profondi? cioè un puntello con: { ok: 1, notOk: { meh: 2 } }se impostato su un dato interno con uno qualsiasi del metodo di clonazione precedente cambierebbe comunque il propnotOk.meh
Nikso

1
Grazie per l'eccellente domanda e risposta @DominikSerafin. Questo illustra perfettamente la guarigione dell'Achille di Vue.js. Come framework gestisce così bene la componentizzazione, ma il passaggio dei dati tra le composizioni è un PITA importante. Basta guardare la nomenclatura da incubo creata. Quindi ora ho bisogno di aggiungere il prefisso a tutti i miei oggetti di scena initialsolo per poterli usare all'interno del mio comp. Sarebbe molto più pulito se potessimo semplicemente passare un prop chiamato dataa qualsiasi comp e fare in modo che propaghi automaticamente i dati localizzati all'interno senza tutta questa initialPropNamefollia.
AJB

1
@DominikSerafin Ho sentito cosa stai dicendo e non ti sbagli. Ma scrivi un'app per la creazione di moduli utilizzando Vue.js e vedrai rapidamente cosa intendo. In realtà sto cambiando la mia tecnica di condivisione dei dati prop --> comp.datadall'utilizzo di Vue-Stash per tutti i dati (o anche Vuex funzionerebbe). Ma ora sto solo rimbalzando i miei dati windowdall'oggetto come facevo prima che i framework JS "moderni" mi imponessero la loro opinione. Quindi sorge la domanda: qual è il punto della proprietà comp.data?
AJB

25

Insieme alla risposta di @ dominik-serafin:

Nel caso in cui stai passando un oggetto, puoi facilmente clonarlo utilizzando l'operatore di diffusione (sintassi ES6):

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

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

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

Ma la cosa più importante è ricordarsi di usare opt. 2 nel caso in cui si passi un valore calcolato, o più di questo un valore asincrono. In caso contrario, il valore locale non verrà aggiornato.

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/vue@2.5.17/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/


Ehi @ igor-parra, sembra che tu abbia inviato risposte duplicate. E potrebbe anche essere utile menzionare che l'operatore di diffusione non è supportato in tutti i browser e potrebbe essere necessario un passaggio di transpiling per una migliore compatibilità. Altrimenti sembra fantastico - grazie per aver aggiunto questo metodo alternativo!
Dominik Serafin

@ dominik-serafin Sì, hai ragione, ho aggiunto un riferimento a ES6. Nelle applicazioni basate su webpack è così facile avere babel preconfigurato per fare pieno uso del moderno javascript.
Igor Parra

recordLocal: [...this.record](nel mio caso il record è un array) non ha funzionato per me - dopo aver caricato e ispezionato il componente vue, recordcontiene gli elementi ed recordLocalera un array vuoto.
Christian

Quando lo uso tramite la proprietà COMPUTED va bene, per me quando provo a impostare all'interno dei dati non funziona = (... Ma l'ho ottenuto usandocomputed
felipe muner

Stai attento. L'operatore di diffusione solo cloni superficiali, quindi per gli oggetti che contengono oggetti o array continuerai a copiare i puntatori invece di ottenere una nuova copia. Uso cloneDeep di lodash.
Cindy Conway

13

Credo che tu lo stia facendo bene perché è ciò che è dichiarato nei documenti.

Definisci una proprietà di dati locali che utilizzi il valore iniziale del prop come valore iniziale

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


4
Per gli oggetti di scena di passaggio che va bene. In questo caso è un oggetto, quindi cambiarlo influenzerà il genitore. Da quella pagina: "Nota che gli oggetti e gli array in JavaScript vengono passati per riferimento, quindi se il prop è un array o un oggetto, la modifica dell'oggetto o dell'array stesso all'interno del componente figlio influenzerà lo stato genitore."
Jake

Questo funziona per me ed è chiaramente documentato. questa riga nella risposta sopra è in conflitto con la documentazione di vue, per quanto ne so "Attenzione: questo metodo non è raccomandato. Renderà i tuoi componenti imprevedibili. Se devi impostare i dati genitore dai componenti figli, usa la gestione dello stato come Vuex o usa "v-model". "
Nathaniel Rink

Quindi, dopo 4 anni e centinaia di ore di curva di apprendimento, sono tornato al punto in cui ho iniziato a far rimbalzare i vars windowsull'oggetto.
AJB
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.