Jak słuchać zmian „rekwizytów”


256

w VueJs 2.0 nie mogę znaleźć żadnych haków, które mogłyby nasłuchiwać propszmian.

Czy VueJ ma takie haki? onPropsUpdated() lub podobne?

Aktualizacja

Jak sugerował @wostex, próbowałem watchmojej własności, ale nic się nie zmieniło. Potem zdałem sobie sprawę, że mam specjalny przypadek:

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

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

Podaję, myPropże składnik nadrzędny odbiera childskładnik. To watch: {myProp: ...}nie działa.



1
Nie jestem pewien, czy dobrze rozumiem twoją sprawę. Czy możesz wyjaśnić, jaki jest twój dokładny cel? „Kiedy coś się dzieje, chcę, żeby coś się stało tutaj (gdzie?)”. Czy próbujesz ręcznie zmienić nadrzędną wartość prop? Jeśli tak, to zupełnie inny przypadek.
Egor Stambakio

@wostex, oto CodePen . Złą rzeczą jest to, że w piórze działa ...
Amio.io

1
Rozumiem, problem jest gdzie indziej. Pokaż mi swój kod, popatrzę.
Egor Stambakio

1
Dzień dobry. Tutaj: ChannelDetail.vue nigdzie nie importujesz komponentu FacebookPageDetail, ale deklarujesz go: gist.github.com/zatziky/ ... Myślę, że powinien to być FacebookChannelDetail.vue importowany do ChannelDetail.vue jako FacebookPageDetail Poza tym wygląda dobrze
Egor Stambakio

Odpowiedzi:


320

Możesz watchrekwizyty wykonać kod po zmianach rekwizytów:

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
Dzięki za wostex, już próbowałem watch. Dzięki twojemu przykładowi zrozumiałem, że moja sprawa jest trochę inna. Zaktualizowałem swoje pytanie.
Amio.io,

1
zobacz, jak to wygląda jak componentWillReceiveProps (), dzięki na przykład: D
yussan

@yussan tak, ale dotyczy tylko jednego rekwizytu, którego musisz śledzić.
Egor Stambakio

1
Wiem, że odradza się oglądanie, ponieważ zwykle lepiej jest użyć obliczonej właściwości, ale czy są jakieś problemy z wydajnością podczas używania zegarka?
SethWhite

1
@SethWhite Z tego, co rozumiem, jak reaktywność jest obsługiwana w Vue przy użyciu wzorca obserwatora, obserwatorzy niekoniecznie są mniej wydajni niż dane obliczone lub inne dane reaktywne. W przypadkach, gdy wartość właściwości zależy od wielu wartości, obliczona rekwizyt uruchomi jedną funkcję w stosunku do osobnego wywołania zwrotnego dla każdej wartości, od której zależy wynik. Myślę, że w najczęstszych przypadkach użycia obliczona właściwość miałaby pożądany rezultat.
plong0

83

Próbowałeś tego?

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
To zadziałało dla mnie, gdzie odpowiedź nie zadziałała - może to aktualizacja. Dzięki BearInBox :)
Grant

2
natychmiast: prawda naprawiła to dla mnie
basbase

2
Ustawienie zegarka w ten sposób również mnie naprawiło, dzięki!
adamk22

2
Dla mnie też działa. To powinna być zaakceptowana odpowiedź
titou10

1
dla mnie też działało, podczas gdy zaakceptowana odpowiedź nie. +1
Roberto,

25

On,

w moim przypadku potrzebowałem rozwiązania, w którym za każdym razem zmieniałyby się rekwizyty, musiałem ponownie przeanalizować moje dane. Byłem zmęczony robieniem osobnego obserwatora dla wszystkich moich rekwizytów, więc użyłem tego:

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

Kluczowym punktem, który należy odnieść od tego przykładu, jest użycie deep: true więc nie tylko ogląda rekwizyty $, ale także zagnieżdżone wartości, takie jak npprops.myProp

Możesz dowiedzieć się więcej o tej rozszerzonej opcji oglądania tutaj: https://vuejs.org/v2/api/#vm-watch


Cieszę się, że zapłacę naprzód! Powodzenia!
JoeSchr

7

Musisz zrozumieć, jaką masz hierarchię komponentów i sposób przekazywania rekwizytów, na pewno twoja sprawa jest wyjątkowa i zwykle nie spotykana przez programistów.

Komponent nadrzędny -myProp-> Komponent podrzędny -myProp-> Komponent wnuka

Jeśli myProp zostanie zmieniony w komponencie nadrzędnym, zostanie to odzwierciedlone w komponencie podrzędnym.

A jeśli myProp zostanie zmieniony w komponencie potomnym, zostanie to odzwierciedlone w komponencie wnuka.

Jeśli więc myProp zostanie zmieniony w komponencie nadrzędnym, zostanie to odzwierciedlone w komponencie wnuka. (na razie w porządku).

Dlatego w hierarchii nie musisz nic robić, rekwizyty będą z natury reaktywne.

Teraz mówimy o awansie w hierarchii

Jeśli myProp zostanie zmieniony w składniku grandChild, nie zostanie odzwierciedlony w komponencie potomnym. Musisz użyć modyfikatora .sync w potomku i emitować zdarzenie ze składnika grandChild.

Jeśli myProp zostanie zmieniony w komponencie potomnym, nie zostanie odzwierciedlony w komponencie macierzystym. Musisz użyć modyfikatora .sync w obiekcie nadrzędnym i wyemitować zdarzenie ze składnika potomnego.

Jeśli myProp zostanie zmieniony w składniku grandChild, nie zostanie odzwierciedlony w składniku nadrzędnym (oczywiście). Musisz użyć podrzędnego modyfikatora .sync i wyemitować zdarzenie z komponentu wnuka, a następnie obejrzeć prop w komponencie podrzędnym i wyemitować zdarzenie przy zmianie, które jest nasłuchiwane przez komponent nadrzędny za pomocą modyfikatora .sync.

Zobaczmy trochę kodu, aby uniknąć zamieszania

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>

Ale po tym nie pomożesz zauważyć krzyczących ostrzeżeń mówiących vue

„Unikaj bezpośredniego modyfikowania rekwizytu, ponieważ wartość zostanie nadpisana za każdym razem, gdy element nadrzędny zostanie ponownie renderowany”.

Ponownie, jak wspomniałem wcześniej, większość programistów nie napotyka tego problemu, ponieważ jest to anty-wzór. Dlatego otrzymujesz to ostrzeżenie.

Ale w celu rozwiązania problemu (zgodnie z projektem). Uważam, że musisz wykonać powyższe czynności (włamać się szczerze mówiąc). Nadal zalecam, aby przemyśleć swój projekt i uczynić go mniej podatnym na błędy.

Mam nadzieję, że to pomoże.


6

Nie jestem pewien, czy to rozwiązałeś (i czy dobrze rozumiem), ale oto mój pomysł:

Jeśli rodzic otrzyma myProp, a Ty chcesz przekazać go dziecku i obejrzeć w dziecku, wówczas rodzic musi mieć kopię myProp (bez referencji).

Spróbuj tego:

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

i w html:

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

tak naprawdę musisz być bardzo ostrożny podczas pracy nad złożonymi kolekcjami w takich sytuacjach (przekazywanie kilka razy)


Dobry pomysł. W tej chwili nie mogę tego zweryfikować. Kiedy znów będę pracować z vue, sprawdzę to.
Amio.io,

1
dziękuję @ m.cichacz, popełniłem ten sam błąd i szybko naprawiłem dzięki waszej sugestii, aby przekazać kopię dziecku
nieokreślony błąd

4

do wiązania dwukierunkowego należy użyć modyfikatora .sync

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

więcej szczegółów...

i musisz użyć właściwości watch w komponencie potomnym, aby nasłuchiwać i aktualizować wszelkie zmiany

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

To prawda, że ​​jest to nowy sposób obserwowania zmian rekwizytów w komponentach potomnych.
Amio.io,

2

Pracuję z obliczoną właściwością, taką jak:

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

Zasoby są w tym przypadku własnością:

props: [ 'resources' ]

1

Dla mnie jest to grzeczne rozwiązanie, aby uzyskać jedną konkretną zmianę (rekwizyty) i stworzyć z niej logikę

Chciałbym użyć propsi computedwłaściwości zmiennych , aby utworzyć logikę po, aby otrzymać zmiany

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

Możemy więc użyć _objectDetail na renderowaniu HTML

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

lub w jakiś sposób:

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

0

Możesz użyć trybu zegarka do wykrywania zmian:

Rób wszystko na poziomie atomowym. Więc najpierw sprawdź, czy sama metoda oglądania jest wywoływana, czy nie, pociesząc coś w środku. Po ustaleniu, że zegarek jest wywoływany, zniszcz go logiką biznesową.

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

0

Używam propsi zmiennych computedwłaściwości, jeśli muszę utworzyć logikę po, aby otrzymać zmiany

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

        if (someValidation)
        ...
    }
}

-1

jeśli myProp jest obiektem, nie można go normalnie zmienić. więc zegarek nigdy się nie uruchomi. powodem, dla którego myProp się nie zmienia, jest to, że w większości przypadków po prostu ustawiasz niektóre klucze myProp. sam myProp wciąż jest tym jedynym. spróbuj oglądać rekwizyty myProp, takie jak „myProp.a”, powinno działać.


-1

Funkcja zegarka powinna zostać umieszczona w komponencie potomnym. Nie rodzicem.


-1

@JoeSchr ma dobrą odpowiedź. oto inny sposób na zrobienie tego, jeśli nie chcesz „głęboko: prawda”.

 mounted() {
    this.yourMethod();
    // re-render any time a prop changes
    Object.keys(this.$options.props).forEach(key => {
      this.$watch(key, this.yourMethod);
    });
  },
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.