Як слухати зміни «реквізиту»


256

У документах VueJs 2.0 я не можу знайти жодних гачків, які б слухали propsзміни.

У VueJs такі гачки onPropsUpdated()чи подібні?

Оновлення

Як запропонував @wostex, я спробував watchвласність, але нічого не змінилося. Тоді я зрозумів, що у мене є особливий випадок:

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

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

Я передаю, myPropщо батьківський компонент отримує childкомпонент. Тоді watch: {myProp: ...}не працює.



1
Я не впевнений, що я правильно розумію вашу справу. Чи можете ви пояснити, яка ваша точна мета? "Коли щось трапляється, я хочу, щоб тут (де?) Щось сталося суворо". Ви намагаєтесь вручну змінити значення батьківської опори? Якщо так, то це зовсім інший випадок.
Єгор Стамбакіо

@wostex, ось CodePen . Погано в тому, що в ручці це працює ...
Amio.io

1
Я бачу, проблема десь в іншому місці. Покажіть мені свій код, я буду дивитись.
Єгор Стамбакіо

1
Привіт. Ось: ChannelDetail.vue ви не імпортуєте компонент FacebookPageDetail куди завгодно, але декларуєте це: gist.github.com/zatziky/… Я думаю, це має бути FacebookChannelDetail.vue, імпортований у ChannelDetail.vue як FacebookPageDetail, окрім того, що це виглядає нормально
Egor Стамбакіо

Відповіді:


320

Ви можете watchреквізити для виконання деякого коду при зміні реквізиту:

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 для wostex, я вже пробував watch. Завдяки вашому прикладу я зрозумів, що мій випадок трохи інший. Я оновив своє запитання.
Amio.io

1
дивіться, це виглядає як компонентWillReceiveProps (), спасибі, наприклад: D
Юссан

@yussan так, але він застосовується до однієї опори, яку потрібно відстежувати.
Єгор Стамбакіо

1
Я знаю, що дивитися не рекомендується, тому що зазвичай краще використовувати обчислюване властивість, але чи є проблеми з роботою при використанні годинника?
SethWhite

1
@SethWhite З того, що я розумію, як реактивність обробляється у Vue, використовуючи схему спостереження, спостерігачі не обов'язково менш ефективні, ніж обчислені чи інші реактивні дані. У випадках, коли значення властивості залежить від багатьох значень, обчислювана опора буде виконувати одну функцію проти окремого зворотного виклику годинника для кожного зі значень, від яких залежить результат. Я думаю, що у більшості випадків використання обчислювальна властивість мала б бажаний результат.
plong0

83

Ви пробували це?

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
Це працювало для мене, де відповіді не було - можливо, це оновлення. Дякую BearInBox :)
Грант

2
негайне: правда виправлено це для мене
базар

2
Встановити такий годинник - це те, що і зафіксувало мене, дякую!
adamk22

2
Працює і для мене. Це має бути прийнята відповідь
titou10

1
працював і на мене, тоді як прийнята відповідь не відповіла. +1
Роберто

25

Він,

в моєму випадку мені потрібно було рішення, де в будь-який час змінився будь-який реквізит, мені потрібно було ще раз проаналізувати свої дані. Я втомився робити відокремлений спостерігач для всіх своїх реквізитів, тому я використав це:

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

Ключовим моментом, щоб відібратись від цього прикладу, є використання, deep: trueщоб він не тільки спостерігав $ реквізит, але і це вкладені значення, наприклад, наприкладprops.myProp

Ви можете дізнатися більше про ці параметри розширеного перегляду тут: https://vuejs.org/v2/api/#vm-watch


Радий заплатити вперед! Удачі!
JoeSchr

7

Вам потрібно зрозуміти, ієрархія складових, яку ви маєте, і як ви передаєте реквізит, безумовно, ваш випадок особливий і зазвичай не стикається з розробниками.

Батьківський компонент -myProp-> Дочірній компонент -myProp-> Компонент онука

Якщо myProp буде змінено на батьківський компонент, це відобразиться і в дочірнім компоненті.

І якщо myProp буде змінено на дочірній компонент, це відобразиться і на компоненті онука.

Отже, якщо myProp буде змінено у батьківському компоненті, це відобразиться на компоненті внука. (все йде нормально).

Тому вниз по ієрархії, вам не потрібно нічого робити, реквізит буде за своєю суттю реактивним.

Зараз ми говоримо про підйом в ієрархії

Якщо myProp змінено в компоненті grandChild, він не відображатиметься в дочірньому компоненті. Ви повинні використовувати .sync-модифікатор у дочірніх та випромінювати події з компонента grandChild.

Якщо myProp змінено в дочірньому компоненті, він не відображатиметься в батьківському компоненті. Ви повинні використовувати .sync-модифікатор у батьківському і випромінювати події з дочірнього компонента.

Якщо myProp буде змінено в компоненті grandChild, він не відображатиметься в батьківському компоненті (очевидно). Вам потрібно використовувати дочірний модифікатор .sync та випромінювати подію з компонента онука, а потім спостерігати за опорою на дочірній компонент та випромінювати подію на зміну, яку слухає батьківський компонент за допомогою модифікатора .sync.

Давайте подивимось якийсь код, щоб уникнути плутанини

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>

Але після цього ви не допоможете помітити кричущі попередження про висловлювання

"Уникайте мутації опори безпосередньо, оскільки значення буде перезаписано кожного разу, коли батьківський компонент повторно відображає."

Знову, як я вже згадував, більшість розробників не стикаються з цим питанням, тому що це анти-модель. Ось чому ви отримуєте це попередження.

Але для того, щоб вирішити своє питання (відповідно до вашого дизайну). Я вважаю, що ви повинні виконати вищезгадану роботу навколо (хак, щоб бути чесним). Я все ж рекомендую вам переосмислити свій дизайн і зробити менш схильним до помилок.

Я сподіваюся, що це допомагає.


6

Не впевнений, чи вирішили ви це (і якщо я правильно зрозумів), але ось моя ідея:

Якщо батько отримує myProp, і ви хочете, щоб він передав дитині і спостерігав за ним у дитини, тоді батько повинен мати копію myProp (не посилання).

Спробуйте це:

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

і в html:

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

насправді ви повинні бути дуже обережними, працюючи над складними колекціями в таких ситуаціях (пропускаючи кілька разів)


Гарна ідея. Я не можу просто підтвердити це на даний момент. Коли я знову працюватиму з vue, я перевірю це.
Amio.io

1
дякую @ m.cichacz, зробив ту саму помилку і негайно виправив завдяки вашій пропозиції передати копію дитині
undefinederror

4

для двостороннього прив'язки вам потрібно використовувати модифікатор .sync

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

Детальніше...

і вам потрібно використовувати властивість watch у дочірньому компоненті, щоб слухати та оновлювати будь-які зміни

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

Правда, це новий спосіб спостерігати за зміною реквізиту в дочірніх компонентах.
Amio.io

2

Я працюю з обчисленою властивістю, наприклад:

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

Ресурси в цьому випадку є властивістю:

props: [ 'resources' ]

1

Для мене це ввічливе рішення отримати одну конкретну зміну опори (-ів) та створити з нею логіку

Я б використовував propsі computedвластивості змінних для створення логіки після отримання змін

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

Отже, ми можемо використовувати _objectDetail в html-рендері

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

або в якомусь методі:

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

0

Ви можете використовувати режим перегляду для виявлення змін:

Робіть все на атомному рівні. Тож спочатку перевірте, чи викликається сам метод перегляду, чи втішаєте щось всередині. Як тільки буде встановлено, що годинник викликається, розбийте його за своєю діловою логікою.

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

0

Я використовую propsі computedвластивості змінних, якщо мені потрібно створити логіку після отримання змін

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

        if (someValidation)
        ...
    }
}

-1

якщо myProp є об'єктом, він може не змінюватися звичайно. так що дивитися ніколи не буде спрацьовувати. Причина, чому myProp не змінюється, полягає в тому, що ви просто встановлюєте деякі клавіші myProp у більшості випадків. сам myProp все ще є одним. спробуйте переглянути реквізити myProp, як-от "myProp.a", це повинно працювати.


-1

Функція годинника повинна розміщуватися в дочірніх компонентах. Не батьківський.


-1

@JoeSchr має хорошу відповідь. ось ще один спосіб зробити це, якщо ви не хочете "глибоко: правда".

 mounted() {
    this.yourMethod();
    // re-render any time a prop changes
    Object.keys(this.$options.props).forEach(key => {
      this.$watch(key, this.yourMethod);
    });
  },
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.