Cara mendengarkan perubahan 'alat peraga'

257

Dalam VueJs 2.0 docs saya tidak dapat menemukan kait yang akan mendengarkan propsperubahan.

Apakah VueJ memiliki kait seperti itu onPropsUpdated()atau serupa?

Memperbarui

Seperti saran @wostex, saya mencoba watchproperti saya tetapi tidak ada yang berubah. Kemudian saya menyadari bahwa saya punya kasus khusus:

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

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

Saya menyampaikan myPropbahwa komponen induk menerima ke childkomponen. Maka watch: {myProp: ...}tidak berfungsi.

Amio.io
sumber
1
Saya tidak yakin saya memahami kasus Anda dengan benar. Bisakah Anda menjelaskan apa tujuan sebenarnya Anda? "Ketika sesuatu terjadi, saya ingin sesuatu terjadi dengan kaku di sini (di mana?)". Apakah Anda mencoba mengubah nilai prop orangtua secara manual? Jika ya ini kasus yang sama sekali berbeda.
Egor Stambakio
@wostex, ini CodePen . Yang buruk adalah bahwa di dalam pena itu berfungsi ...
Amio.io
1
Begitu ya, masalahnya ada di tempat lain. Tunjukkan kode Anda, saya akan melihatnya.
Egor Stambakio
1
Halo. Di sini: ChannelDetail.vue Anda tidak mengimpor komponen FacebookPageDetail di mana pun, tetapi mendeklarasikannya: gist.github.com/zatziky/... Saya kira itu seharusnya FacebookChannelDetail.vue yang diimpor ke ChannelDetail.vue sebagai FacebookPageDetail Selain itu terlihat baik
Egor Stambakio

Jawaban:

322

Anda dapat menggunakan properti watchuntuk mengeksekusi beberapa kode pada perubahan properti:

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>

Egor Stambakio
sumber
1
Terima kasih untuk wostex, saya sudah mencoba watch. Karena teladan Anda, saya menyadari bahwa kasus saya sedikit berbeda. Saya telah memperbarui pertanyaan saya.
Amio.io
1
lihat itu terlihat seperti componentWillReceiveProps (), terima kasih misalnya: D
yussan
@Yussan ya, tapi itu diterapkan pada satu alat yang Anda harus melacak.
Egor Stambakio
1
Saya tahu arloji tidak disarankan karena biasanya lebih baik menggunakan properti yang dihitung, tetapi apakah ada masalah kinerja dengan menggunakan arloji?
SethWhite
1
@SethWhite Dari apa yang saya pahami tentang bagaimana reaktivitas ditangani dalam Vue menggunakan pola pengamat, pengamat tidak selalu kurang efisien daripada data reaktif yang dihitung atau lainnya. Dalam kasus di mana nilai properti bergantung pada banyak nilai, prop yang dikomputasi akan menjalankan fungsi tunggal vs callback arloji terpisah untuk masing-masing nilai yang bergantung pada hasil. Saya pikir dalam kebanyakan kasus penggunaan, properti yang dihitung akan memiliki hasil yang diinginkan.
plong0
83

Sudahkah Anda mencoba ini?

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

BearInBox
sumber
7
Ini bekerja untuk saya, di mana jawabannya tidak - mungkin itu adalah pembaruan. Terima kasih BearInBox :)
Berikan
2
segera: benar diperbaiki untuk saya
basbase
2
Menyetel jam tangan seperti ini adalah yang memperbaikinya bagi saya juga, terima kasih!
adamk22
2
Bekerja untuk saya juga. Ini harus menjadi jawaban yang diterima
titou10
1
bekerja untuk saya juga sedangkan jawaban yang diterima tidak. +1
Roberto
25

Dia,

dalam kasus saya, saya membutuhkan solusi di mana setiap alat peraga akan berubah, saya perlu mengurai data saya lagi. Saya bosan membuat pengamat yang terpisah untuk semua alat peraga saya, jadi saya menggunakan ini:

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

Poin kunci untuk mengambil dari contoh ini adalah menggunakan deep: true sehingga tidak hanya menonton $ alat peraga tetapi juga nilai-nilai yang bersarang seperti misalnyaprops.myProp

Anda dapat mempelajari lebih lanjut tentang opsi jam tangan yang diperluas ini di sini: https://vuejs.org/v2/api/#vm-watch

JoeSchr
sumber
Senang membayarnya ke depan! Semoga berhasil!
JoeSchr
7

Anda perlu memahami, hierarki komponen yang Anda miliki dan bagaimana Anda melewati alat peraga, pasti kasus Anda istimewa dan biasanya tidak ditemui oleh para pengembang.

Komponen Induk -myProp-> Komponen Anak -myProp-> Komponen Cucu

Jika myProp diubah dalam komponen induk, itu akan tercermin dalam komponen turunan juga.

Dan jika myProp diubah dalam komponen anak itu akan tercermin dalam komponen cucu juga.

Jadi jika myProp diubah dalam komponen induk maka akan tercermin dalam komponen cucu. (sejauh ini baik).

Oleh karena itu turun hierarki Anda tidak perlu melakukan alat peraga apa pun akan secara inheren reaktif

Sekarang berbicara tentang naik dalam hierarki

Jika myProp diubah dalam komponen grandChild, itu tidak akan tercermin dalam komponen anak. Anda harus menggunakan pengubah .sync pada child dan memancarkan event dari komponen grandChild.

Jika myProp diubah dalam komponen anak, itu tidak akan tercermin dalam komponen induk. Anda harus menggunakan pengubah .sync di induk dan memancarkan peristiwa dari komponen anak.

Jika myProp diubah dalam komponen grandChild, itu tidak akan tercermin dalam komponen induk (jelas). Anda harus menggunakan anak pengubah .sync dan memancarkan peristiwa dari komponen cucu, kemudian menonton prop dalam komponen anak dan memancarkan peristiwa pada perubahan yang sedang didengarkan oleh komponen induk menggunakan pengubah .sync.

Mari kita lihat beberapa kode untuk menghindari kebingungan

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>

Anak

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

Tetapi setelah ini, Anda tidak akan membantu memperhatikan peringatan teriakan dari vue mengatakan

'Hindari bermutasi prop secara langsung karena nilai akan ditimpa setiap kali komponen induk dirender.'

Sekali lagi seperti yang saya sebutkan sebelumnya sebagian besar devs tidak menemui masalah ini, karena ini merupakan pola anti. Itu sebabnya Anda mendapatkan peringatan ini.

Tetapi untuk menyelesaikan masalah Anda (sesuai dengan desain Anda). Saya percaya Anda harus melakukan pekerjaan di atas (hack jujur). Saya masih menyarankan Anda harus memikirkan kembali desain Anda dan membuatnya kurang rentan terhadap bug.

Saya harap ini membantu.

The Observer Man
sumber
6

Tidak yakin apakah Anda telah menyelesaikannya (dan jika saya memahaminya dengan benar), tetapi inilah gagasan saya:

Jika orang tua menerima myProp, dan Anda ingin meneruskannya ke child dan menontonnya di child, maka orang tua harus memiliki salinan myProp (bukan referensi).

Coba ini:

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

dan dalam html:

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

sebenarnya Anda harus sangat berhati-hati ketika mengerjakan koleksi rumit dalam situasi seperti itu (mewariskan beberapa kali)

m.cichacz
sumber
Ide bagus. Saya tidak bisa memvalidasinya saat ini. Ketika saya bekerja dengan vue lagi, saya akan memeriksanya.
Amio.io
1
terima kasih @ m.cichacz, membuat kesalahan yang sama dan segera memperbaikinya berkat saran Anda untuk memberikan salinannya kepada anak
undefinederror
4

untuk penjilidan dua arah Anda harus menggunakan pengubah .sync

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

keterangan lebih lanjut...

dan Anda harus menggunakan properti arloji dalam komponen anak untuk mendengarkan dan memperbarui segala perubahan

props: ['myprop'],
  watch: { 
    myprop: function(newVal, oldVal) { // watch it
      console.log('Prop changed: ', newVal, ' | was: ', oldVal)
    }
  }
Md Mahamudul Hasan
sumber
Benar, ini cara baru untuk mengawasi perubahan alat peraga dalam komponen anak.
Amio.io
2

Saya bekerja dengan properti yang dihitung seperti:

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

Sumber daya dalam hal ini adalah properti:

props: [ 'resources' ]
Schoofs Wouter
sumber
1

Bagi saya ini adalah solusi yang sopan untuk mendapatkan satu perubahan spesifik dan membuat logika dengannya

Saya akan menggunakan propsdan variabel computedproperti untuk membuat logika setelah menerima perubahan

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

Jadi, kita bisa menggunakan _objectDetail di html render

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

atau dalam beberapa metode:

literallySomeMethod: function() {
   if (this._objectDetail) {
   ....
   }
}
Manuel Alanis
sumber
0

Anda dapat menggunakan mode tontonan untuk mendeteksi perubahan:

Lakukan segalanya pada level atom. Jadi pertama-tama periksa apakah metode menonton itu sendiri dipanggil atau tidak dengan menghibur sesuatu di dalamnya. Setelah ditetapkan bahwa arloji dipanggil, hancurkan dengan logika bisnis Anda.

watch: { 
  myProp: function() {
   console.log('Prop changed')
  }
}
Rahul Sharma
sumber
0

Saya menggunakan propsdan variabel computedproperti jika saya perlu membuat logika setelah menerima perubahan

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

        if (someValidation)
        ...
    }
}
Manuel Alanis
sumber
-1

jika myProp adalah objek, itu tidak dapat diubah seperti biasa. jadi, arloji tidak akan pernah terpicu. alasan mengapa myProp tidak dapat diubah adalah karena Anda hanya mengatur beberapa kunci myProp dalam banyak kasus. myProp itu sendiri masih satu-satunya. cobalah untuk menonton alat peraga dari myProp, seperti "myProp.a", itu seharusnya bekerja.

adocking
sumber
-1

Fungsi jam tangan harus ditempatkan di komponen Anak. Bukan orang tua.

Cong Nguyen
sumber
-1

@ JoSchr punya jawaban yang bagus. di sini adalah cara lain untuk melakukan ini jika Anda tidak ingin 'dalam: benar'.

 mounted() {
    this.yourMethod();
    // re-render any time a prop changes
    Object.keys(this.$options.props).forEach(key => {
      this.$watch(key, this.yourMethod);
    });
  },
Alvin Smith
sumber