Come ascoltare i cambiamenti degli "oggetti di scena"


257

Nei documenti di VueJs 2.0 non riesco a trovare alcun hook che ascolti le propsmodifiche.

VueJs ha tali hook simili onPropsUpdated()o simili?

Aggiornare

Come suggerito da @wostex, ho provato a watchproprietà ma nulla è cambiato. Poi ho capito che ho un caso speciale:

<template>
    <child :my-prop="myProp"></child>
</template>

<script>
   export default {
      props: ['myProp']
   }
</script>

Sto passando myPropche il componente genitore riceve al childcomponente. Quindi il watch: {myProp: ...}non funziona.



1
Non sono sicuro di aver capito bene il tuo caso. Puoi spiegare qual è il tuo obiettivo esatto? "Quando succede qualcosa, voglio che succeda qualcosa qui (dove?)". Stai cercando di cambiare manualmente il valore prop del genitore? Se sì, questo è un caso totalmente diverso.
Egor Stambakio,

@wostex, ecco la CodePen . La cosa brutta è che nella penna funziona ...
Amio.io

1
Vedo, il problema è altrove. Mostrami il tuo codice, darò un'occhiata.
Egor Stambakio,

1
Ciao. Qui: ChannelDetail.vue non importare il componente FacebookPageDetail da nessuna parte, ma dichiararlo: gist.github.com/zatziky/… Immagino che dovrebbe essere FacebookChannelDetail.vue importato in ChannelDetail.vue come FacebookPageDetail A parte quello che sembra a posto
Egor Stambakio,

Risposte:


322

Puoi watchpuntellare per eseguire un po 'di codice in caso di modifiche:

new Vue({
  el: '#app',
  data: {
    text: 'Hello'
  },
  components: {
    'child' : {
      template: `<p>{{ myprop }}</p>`,
      props: ['myprop'],
      watch: { 
      	myprop: function(newVal, oldVal) { // watch it
          console.log('Prop changed: ', newVal, ' | was: ', oldVal)
        }
      }
    }
  }
});
<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app">
  <child :myprop="text"></child>
  <button @click="text = 'Another text'">Change text</button>
</div>


1
Grazie per Wostex, ho già provato watch. Grazie al tuo esempio mi sono reso conto che il mio caso è un po 'diverso. Ho aggiornato la mia domanda.
Amio.io,

1
guarda è simile a componentWillReceiveProps (), grazie ad esempio: D
yussan

@yussan sì, ma è applicato a un singolo oggetto di cui devi tenere traccia.
Egor Stambakio,

1
So che watch è scoraggiato perché di solito è meglio usare una proprietà calcolata, ma ci sono problemi di prestazioni con l'utilizzo di watch?
Seth White

1
@SethWhite Da quanto ho capito di come viene gestita la reattività in Vue usando il modello di osservatore, gli osservatori non sono necessariamente meno efficienti dei dati calcolati o di altri dati reattivi. Nei casi in cui il valore di una proprietà dipende da molti valori, un puntello calcolato eseguirà una singola funzione rispetto a un callback separato per ciascuno dei valori da cui dipende il risultato. Penso che nei casi d'uso più comuni, una proprietà calcolata avrebbe il risultato desiderato.
pl00

83

Hai provato questo?

watch: {
  myProp: {
    // the callback will be called immediately after the start of the observation
    immediate: true, 
    handler (val, oldVal) {
      // do your stuff
    }
  }
}

https://vuejs.org/v2/api/#watch


7
Questo ha funzionato per me, dove la risposta no: forse è un aggiornamento. Grazie BearInBox :)
Concedi il

2
immediato: true riparato per me
basbase

2
Impostare l'orologio in questo modo è ciò che l'ha risolto anche per me, grazie!
adamk22,

2
Funziona anche per me. Questa dovrebbe essere la risposta accettata
titou10

1
ha funzionato anche per me, mentre la risposta accettata no. +1
Roberto,

25

lui,

nel mio caso avevo bisogno di una soluzione in cui ogni volta che un oggetto di scena cambiava, dovevo analizzare nuovamente i miei dati. Ero stanco di creare un osservatore separato per tutti i miei oggetti di scena, quindi ho usato questo:

  watch: {
    $props: {
      handler() {
        this.parseData();
      },
      deep: true,
      immediate: true,
    },

Il punto chiave da sottrarre a questo esempio è l'uso in deep: truemodo che non solo guardi $ props ma anche valori nidificati come ad esprops.myProp

Puoi saperne di più su queste opzioni di orologio estese qui: https://vuejs.org/v2/api/#vm-watch


Felice di pagare in avanti! In bocca al lupo!
JoeSchr,

7

Devi capire, la gerarchia dei componenti che stai avendo e come stai passando oggetti di scena, sicuramente il tuo caso è speciale e di solito non incontrato dagli sviluppatori.

Componente padre -myProp-> Componente figlio -myProp-> Componente nipote

Se myProp viene modificato nel componente padre, si rifletterà anche nel componente figlio.

E se myProp viene modificato nel componente figlio, si rifletterà anche nel componente nipote.

Quindi, se myProp viene modificato nel componente padre, si rifletterà nel componente nipote. (Fin qui tutto bene).

Pertanto, nella gerarchia, non devi fare nulla che gli oggetti di scena siano intrinsecamente reattivi.

Ora parliamo di salire nella gerarchia

Se myProp viene modificato nel componente grandChild non si rifletterà nel componente figlio. Devi usare il modificatore .sync nell'evento figlio ed emettere dal componente grandChild.

Se myProp viene modificato nel componente figlio , non verrà riflesso nel componente padre. Devi usare il modificatore .sync nell'evento genitore ed emettere dal componente figlio.

Se myProp viene modificato nel componente grandChild non si rifletterà nel componente padre (ovviamente). Devi usare il figlio del modificatore .sync ed emettere un evento dal componente nipote, quindi guardare il prop nel componente figlio ed emettere un evento sulla modifica che viene ascoltato dal componente genitore usando il modificatore .sync.

Vediamo un po 'di codice per evitare confusione

Parent.vue

<template>
    <div>
    <child :myProp.sync="myProp"></child>
    <input v-model="myProp"/>
    <p>{{myProp}}</p>
</div>
</template>

<script>

    import child from './Child.vue'

    export default{
        data(){
            return{
                myProp:"hello"
            }
        },
        components:{
            child
        }
    }
</script>

<style scoped>
</style>

Child.vue

<template>
<div>   <grand-child :myProp.sync="myProp"></grand-child>
    <p>{{myProp}}</p>
</div>

</template>

<script>
    import grandChild from './Grandchild.vue'

    export default{
        components:{
            grandChild
        },
        props:['myProp'],
        watch:{
            'myProp'(){
                this.$emit('update:myProp',this.myProp)

            }
        }
    }
</script>

<style>

</style>

Grandchild.vue

<template>
    <div><p>{{myProp}}</p>
    <input v-model="myProp" @input="changed"/>
    </div>
</template>

<script>
    export default{
        props:['myProp'],
        methods:{
            changed(event){
                this.$emit('update:myProp',this.myProp)
            }
        }
    }
</script>

<style>

</style>

Ma dopo questo non ti aiuterai a notare gli avvertimenti urlanti di dire vue

"Evitare di mutare direttamente un oggetto di scena poiché il valore verrà sovrascritto ogni volta che il componente padre viene sottoposto nuovamente a rendering."

Ancora una volta, come ho detto prima, la maggior parte degli sviluppatori non ha riscontrato questo problema, perché è un modello anti. Ecco perché ricevi questo avviso.

Ma per risolvere il tuo problema (secondo il tuo progetto). Credo che devi fare il lavoro sopra descritto (hack per essere onesti). Consiglio comunque di ripensare il tuo design e rendere meno incline ai bug.

Spero possa essere d'aiuto.


6

Non sono sicuro di averlo risolto (e se ho capito bene), ma ecco la mia idea:

Se parent riceve myProp e desideri che passi a child e lo guardi da child, allora parent deve avere una copia di myProp (non riferimento).

Prova questo:

new Vue({
  el: '#app',
  data: {
    text: 'Hello'
  },
  components: {
    'parent': {
      props: ['myProp'],
      computed: {
        myInnerProp() { return myProp.clone(); } //eg. myProp.slice() for array
      }
    },
    'child': {
      props: ['myProp'],
      watch: {
        myProp(val, oldval) { now val will differ from oldval }
      }
    }
  }
}

e in html:

<child :my-prop="myInnerProp"></child>

in realtà devi stare molto attento quando lavori su raccolte complesse in tali situazioni (passando alcune volte)


Buona idea. Non posso semplicemente convalidarlo al momento. Quando lavoro di nuovo con Vue, lo controllerò.
Amio.io,

1
grazie @ m.cichacz, ho fatto lo stesso errore e risolto prontamente grazie al tuo suggerimento di passare una copia al bambino
undefinederror

4

per l'associazione bidirezionale devi usare il modificatore .sync

<child :myprop.sync="text"></child>

più dettagli...

e devi utilizzare la proprietà watch nel componente figlio per ascoltare e aggiornare eventuali modifiche

props: ['myprop'],
  watch: { 
    myprop: function(newVal, oldVal) { // watch it
      console.log('Prop changed: ', newVal, ' | was: ', oldVal)
    }
  }

È vero, questo è il nuovo modo di guardare per i cambiamenti dei puntelli nei componenti figlio.
Amio.io,

2

Lavoro con una proprietà calcolata come:

    items:{
        get(){
            return this.resources;
        },
        set(v){
            this.$emit("update:resources", v)
        }
    },

Le risorse sono in questo caso una proprietà:

props: [ 'resources' ]

1

Per me questa è una soluzione educata per ottenere una o più modifiche specifiche e creare una logica con essa

Vorrei utilizzare propse le computedproprietà delle variabili per creare la logica dopo aver ricevuto le modifiche

export default {
name: 'getObjectDetail',
filters: {},
components: {},
props: {
  objectDetail: { // <--- we could access to this value with this.objectDetail
    type: Object,
    required: true
  }
},
computed: {
  _objectDetail: {
    let value = false
    // ...
    // if || do || while -- whatever logic
    // insert validation logic with this.objectDetail (prop value)
    value = true
    // ...
    return value 
  }
}

Quindi, potremmo usare _objectDetail sul rendering html

<span>
  {{ _objectDetail }}
</span>

o in qualche metodo:

literallySomeMethod: function() {
   if (this._objectDetail) {
   ....
   }
}

0

È possibile utilizzare la modalità orologio per rilevare le modifiche:

Fai tutto a livello atomico. Quindi per prima cosa controlla se il metodo watch stesso viene chiamato o meno consolando qualcosa all'interno. Una volta stabilito che l'orologio viene chiamato, distruggilo con la tua logica aziendale.

watch: { 
  myProp: function() {
   console.log('Prop changed')
  }
}

0

Uso propsle computedproprietà delle variabili e se ho bisogno di creare la logica dopo per ricevere le modifiche

export default {
name: 'getObjectDetail',
filters: {},
components: {},
props: {
    objectDetail: {
      type: Object,
      required: true
    }
},
computed: {
    _objectDetail: {
        let value = false
        ...

        if (someValidation)
        ...
    }
}

-1

se myProp è un oggetto, non può essere modificato normalmente. quindi, l'orologio non verrà mai attivato. il motivo per cui myProp non può essere modificato è che nella maggior parte dei casi hai semplicemente impostato alcune chiavi di myProp. lo stesso myProp è ancora quello. prova a guardare oggetti di scena di myProp, come "myProp.a", dovrebbe funzionare.


-1

La funzione orologio deve essere inserita nel componente figlio. Non genitore.


-1

@JoeSchr ha una buona risposta. ecco un altro modo per farlo se non vuoi "deep: true".

 mounted() {
    this.yourMethod();
    // re-render any time a prop changes
    Object.keys(this.$options.props).forEach(key => {
      this.$watch(key, this.yourMethod);
    });
  },
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.