Comment écouter les changements des «accessoires»


257

Dans les documents VueJs 2.0, je ne trouve aucun crochet qui écouterait les propschangements.

Est-ce que VueJs a des crochets similaires onPropsUpdated()ou similaires?

Mettre à jour

Comme l'a suggéré @wostex, j'ai essayé watchma propriété mais rien n'a changé. Puis j'ai réalisé que j'avais un cas particulier:

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

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

Je passe myPropque le composant parent reçoit au childcomposant. Alors le watch: {myProp: ...}ne fonctionne pas.



1
Je ne suis pas sûr de bien comprendre votre cas. Pouvez-vous expliquer quel est votre objectif exact? "Quand quelque chose arrive, je veux que quelque chose se passe ici (où?)". Essayez-vous de modifier manuellement la valeur de l'accessoire parent? Si oui, c'est un cas totalement différent.
Egor Stambakio

@wostex, voici le CodePen . La mauvaise chose est que dans le stylo ça marche ...
Amio.io

1
Je vois, le problème est ailleurs. Montrez-moi votre code, je vais y jeter un œil.
Egor Stambakio

1
Bonjour. Ici: ChannelDetail.vue vous n'importez le composant FacebookPageDetail nulle part, mais déclarez-le: gist.github.com/zatziky/… Je suppose que ce devrait être FacebookChannelDetail.vue importé dans ChannelDetail.vue en tant que FacebookPageDetail Autre que cela semble bien
Egor Stambakio

Réponses:


322

Vous pouvez watchproposer d'exécuter du code lors des modifications d'accessoires:

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
Thx pour wostex, j'ai déjà essayé avec watch. Grâce à votre exemple, j'ai réalisé que mon cas était un peu différent. J'ai mis à jour ma question.
Amio.io

1
regardez ça ressemble à componentWillReceiveProps (), merci par exemple: D
yussan

@yussan oui, mais il est appliqué à un seul accessoire dont vous devez faire le suivi.
Egor Stambakio

1
Je sais que la montre est déconseillée car il est généralement préférable d'utiliser une propriété calculée, mais y a-t-il des problèmes de performances avec l'utilisation de la montre?
SethWhite

1
@SethWhite D'après ce que je comprends de la façon dont la réactivité est gérée dans Vue en utilisant le modèle d'observateur, les observateurs ne sont pas nécessairement moins efficaces que les données calculées ou autres données réactives. Dans les cas où la valeur d'une propriété dépend de nombreuses valeurs, un accessoire calculé exécutera une seule fonction par rapport à un rappel de surveillance distinct pour chacune des valeurs dont dépend le résultat. Je pense que dans la plupart des cas d'utilisation courants, une propriété calculée aurait le résultat souhaité.
plong0

83

Avez-vous essayé cela?

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
Cela a fonctionné pour moi, où la réponse n'a pas fonctionné - c'est peut-être une mise à jour. Merci BearInBox :)
Grant

2
immédiat: vrai l'a corrigé pour moi
basbase

2
Le réglage de la montre comme ça est ce qui l'a réparé pour moi aussi, merci!
adamk22

2
Fonctionne aussi pour moi. Cela devrait être la réponse acceptée
titou10

1
travaillé pour moi aussi alors que la réponse acceptée ne fonctionnait pas. +1
Roberto

25

Il,

dans mon cas, j'avais besoin d'une solution où chaque fois que les accessoires changeraient, je devais analyser à nouveau mes données. J'étais fatigué de faire un observateur séparé pour tous mes accessoires, alors j'ai utilisé ceci:

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

Le point clé à retenir de cet exemple est d'utiliser deep: trueafin qu'il regarde non seulement $ props mais aussi ses valeurs imbriquées comme par exempleprops.myProp

Vous pouvez en savoir plus sur ces options de surveillance étendue ici: https://vuejs.org/v2/api/#vm-watch


Heureux de payer en avant! Bonne chance!
JoeSchr

7

Vous devez comprendre, la hiérarchie des composants que vous avez et comment vous passez des accessoires, votre cas est certainement spécial et n'est généralement pas rencontré par les développeurs.

Composant parent -myProp-> Composant enfant -myProp-> Composant petit-enfant

Si myProp est modifié dans le composant parent, il sera également reflété dans le composant enfant.

Et si myProp est modifié dans le composant enfant, il sera également reflété dans le composant petit-enfant.

Donc, si myProp est modifié dans le composant parent, il sera reflété dans le composant petit-enfant. (jusqu'ici tout va bien).

Par conséquent, dans la hiérarchie, vous n'avez rien à faire, les accessoires seront intrinsèquement réactifs.

Parlons maintenant de monter dans la hiérarchie

Si myProp est modifié dans le composant grandChild, il ne sera pas reflété dans le composant enfant. Vous devez utiliser le modificateur .sync dans l'enfant et émettre un événement à partir du composant grandChild.

Si myProp est modifié dans le composant enfant, il ne sera pas reflété dans le composant parent. Vous devez utiliser le modificateur .sync dans le parent et émettre un événement à partir du composant enfant.

Si myProp est modifié dans le composant grandChild, il ne sera pas reflété dans le composant parent (évidemment). Vous devez utiliser le modificateur .sync enfant et émettre un événement à partir du composant petit-enfant, puis regarder l'accessoire dans le composant enfant et émettre un événement lors de la modification qui est écouté par le composant parent à l'aide du modificateur .sync.

Voyons un peu de code pour éviter toute confusion

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>

Petitchild.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>

Mais après cela, vous n'aurez pas remarqué les avertissements hurlants de vue disant

'Évitez de muter directement un accessoire car la valeur sera écrasée chaque fois que le composant parent sera rendu.'

Encore une fois, comme je l'ai mentionné plus tôt, la plupart des développeurs ne rencontrent pas ce problème, car c'est un anti pattern. C'est pourquoi vous obtenez cet avertissement.

Mais afin de résoudre votre problème (selon votre conception). Je crois que vous devez faire le travail ci-dessus (pirater pour être honnête). Je vous recommande tout de même de repenser votre conception et de la rendre moins sujette aux bugs.

J'espère que ça aide.


6

Je ne sais pas si vous l'avez résolu (et si je comprends bien), mais voici mon idée:

Si le parent reçoit myProp, et que vous voulez qu'il passe à l'enfant et le regarde dans l'enfant, alors le parent doit avoir une copie de myProp (pas une référence).

Essaye ça:

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

et en html:

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

en fait, vous devez être très prudent lorsque vous travaillez sur des collections complexes dans de telles situations (en passant plusieurs fois)


Bonne idée. Je ne peux pas simplement le valider pour le moment. Quand je retravaillerai avec vue, je vérifierai.
Amio.io

1
merci @ m.cichacz, fait la même erreur et corrigé rapidement grâce à votre suggestion de transmettre une copie à l'enfant
undefinederror

4

pour la liaison bidirectionnelle, vous devez utiliser le modificateur .sync

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

plus de détails...

et vous devez utiliser la propriété watch dans le composant enfant pour écouter et mettre à jour les modifications

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

Certes, c'est la nouvelle façon de surveiller les changements d'accessoires dans les composants enfants.
Amio.io

2

Je travaille avec une propriété calculée comme:

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

Les ressources sont dans ce cas une propriété:

props: [ 'resources' ]

1

Pour moi, c'est une solution polie pour obtenir un changement spécifique d'accessoires et créer une logique avec

J'utiliserais propset les computedpropriétés des variables pour créer une logique après avoir reçu les modifications

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

Donc, nous pourrions utiliser _objectDetail sur le rendu html

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

ou dans une certaine méthode:

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

0

Vous pouvez utiliser le mode montre pour détecter les changements:

Faites tout au niveau atomique. Vérifiez donc d'abord si la méthode de surveillance elle-même est appelée ou non en consolant quelque chose à l'intérieur. Une fois qu'il a été établi que la montre est appelée, écrasez-la avec votre logique métier.

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

0

J'utilise propsles computedpropriétés et les variables si j'ai besoin de créer une logique après pour recevoir les modifications

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

        if (someValidation)
        ...
    }
}

-1

si myProp est un objet, il ne peut pas être modifié normalement. ainsi, la montre ne sera jamais déclenchée. la raison pour laquelle myProp ne doit pas être modifiée est que vous venez de définir quelques clés de myProp dans la plupart des cas. le myProp lui-même est toujours celui-là. essayez de regarder les accessoires de myProp, comme "myProp.a", cela devrait fonctionner.


-1

La fonction de surveillance doit être placée dans le composant enfant. Pas parent.


-1

@JoeSchr a une bonne réponse. voici une autre façon de le faire si vous ne voulez pas «profond: vrai».

 mounted() {
    this.yourMethod();
    // re-render any time a prop changes
    Object.keys(this.$options.props).forEach(key => {
      this.$watch(key, this.yourMethod);
    });
  },
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.