Vue.js - İç içe veriyi düzgün şekilde izleme


413

Düzgün bazı prop varyasyon izlemek için nasıl anlamaya çalışıyorum. Bir ajax çağrısından veri almak, bir nesnenin içine veri koymak ve benim uygulama basitleştirilmesi altında bir v-yönergesi ile bazı alt bileşen oluşturmak için kullanmak bir üst bileşen (.vue dosyaları) var:

<template>
    <div>
        <player v-for="(item, key, index) in players"
            :item="item"
            :index="index"
            :key="key"">
        </player>
    </div>
</template>

... sonra iç <script>etiket:

 data(){
     return {
         players: {}
 },
 created(){
        let self = this;
        this.$http.get('../serv/config/player.php').then((response) => {
            let pls = response.body;
            for (let p in pls) {
                self.$set(self.players, p, pls[p]);
            }
    });
}

öğe nesneleri şu şekildedir:

item:{
   prop: value,
   someOtherProp: {
       nestedProp: nestedValue,
       myArray: [{type: "a", num: 1},{type: "b" num: 6} ...]
    },
}

Şimdi, çocuğumun "player" bileşeni içinde herhangi bir Item'in özellik varyasyonunu izlemeye çalışıyorum ve kullanıyorum:

...
watch:{
    'item.someOtherProp'(newVal){
        //to work with changes in "myArray"
    },
    'item.prop'(newVal){
        //to work with changes in prop
    }
}

Çalışıyor ama benim için biraz zor görünüyor ve bunu yapmanın doğru yolu olup olmadığını merak ediyordum. Amacım bazı eylemi her zaman yapmaktır propdeğişiklik veya myArrayyeni unsurlar veya mevcut olanları içindeki bazı varyasyon alır. Herhangi bir öneri takdir edilecektir.


9
"item.someOtherProp": function (newVal, oldVal){@Ron'un önerdiği gibi kullanın .
Reiner

1
@ Reiner tam olarak bu soruda kaçınmaya çalıştığı şeydi; ES5 sözdizimi ile aynı şey. Aslında bir bilgisayar izlemede ek bir yük yoktur ve çeşitli durumlarda kullanışlı bir tekniktir.
craig_h

1
keysBir nesnenin tümünde bir değişiklik izlemek mümkün müdür ? Örnek:"item.*": function(val){...}
Charlie

Yanıtlar:


622

Bunun için derin bir gözlemci kullanabilirsiniz :

watch: {
  item: {
     handler(val){
       // do stuff
     },
     deep: true
  }
}

Bu artık itemdizideki nesnelerdeki değişiklikleri ve dizinin kendisine eklenenleri ( Vue.set ile kullanıldığında ) algılayacaktır . İşte bir JSFiddle: http://jsfiddle.net/je2rw3rs/

DÜZENLE

En üst düzey nesnedeki her değişikliği izlemek istemiyorsanız ve iç içe geçmiş nesneleri doğrudan izlemek için daha az garip bir sözdizimi istiyorsanız, computedbunun yerine basitçe izleyebilirsiniz :

var vm = new Vue({
  el: '#app',
  computed: {
    foo() {
      return this.item.foo;
    }
  },
  watch: {
    foo() {
      console.log('Foo Changed!');
    }
  },
  data: {
    item: {
      foo: 'foo'
    }
  }
})

İşte JSFiddle: http://jsfiddle.net/oa07r5fw/


2
Bu şekilde, herhangi bir pervane varyasyonu olduğunda "işleyici" çağrılır, amacım, değişikliklerin "someOtherProp" içinde ayrı olarak "prop" veya myArray içinde olup olmadığını gözlemlemek için işleyicileri ayırmaktır
Plastic

9
Sadece belirli iç içe nesnelerdeki değişiklikleri izlemek istiyorsanız, yaptığınız şey iyi olur. Daha az garip bir sözdizimi istiyorsanız computedbunun yerine izleyebilirsiniz : jsfiddle.net/c52nda7x
craig_h

Tam olarak aradığım şey, şimdi bana çok mantıklı geliyor: iç içe pervaneyi döndüren ve onu izleyen bir bilgisayar özelliği oluşturun. Çok teşekkürler.
Plastik

Sadece eklemek istiyorum - ikinci seçeneğin orijinal izleyiciden çok farklı olmadığını. Dahili olarak hesaplanan bir özellik, işlevdeki tüm referanslı nesneler / değerler üzerinde sadece işlevin sonucuna bir veri değeri ayarlayan bir izleyicidir. - Yani kodu sadece aynı etkiyle biraz daha kıvrımlı hale getirmiş olursunuz.
Falco

22
@Falco Buraya bir yorumda cevap buldum . peerbolte, saat adını tek tırnak içine alarak yapılabileceğini belirtti. Bunu test ettim ve işe yarıyor. Yani bu durumda watch: { 'item.foo' : function(newVal, oldVal) { // do work here }} oldukça kaygan olurdu . Vue'yi seviyorum!
Ron C

436

Başka bir iyi yaklaşım ve biraz daha zarif olan aşağıdaki gibidir:

 watch:{
     'item.someOtherProp': function (newVal, oldVal){
         //to work with changes in someOtherProp
     },
     'item.prop': function(newVal, oldVal){
         //to work with changes in prop
     }
 }

(Bu yaklaşımı buradaki yorumda @peerbolte'den öğrendim )


47
çok daha zarif (sadece biraz değil): S. Bu yaygın bir kullanım örneği olduğundan Vue.js belgelerinde olmalıdır. Saatlerce bir çözüm arıyordum, cevabınız için teşekkürler.
Claudiu

11
@symba İnsanların bunun OP'nin sadece ES5sözdiziminden kaçınmak istediği şey olduğunu fark etmemesi ilginç . Tabii ki ES2015 method definitionkendisinin çok zarif olmadığını iddia edebiliriz , ancak bir tür soruyu özlüyor, bu da adı kaydırmayı nasıl önleyeceğidir. Çoğu insanın zaten cevabı içeren orijinal soru üzerinde
parladığını

1
@craig_h süper ilginç nokta. Başlangıçta iç içe bir pervane izlemek için iyi bir yol bulmak için saatlerce avlanıyordum. Ve bu soru başlığı bulduğum en yakın soru oldu, ancak sorunun gövdesinde alıntılanmış bir pervane listelediğini, ancak cevapları çok zarif bulmadığını özledim. Sonunda alıntılanan prop yaklaşımını başka bir sorunun yorumunda buldum ve sadece SO üzerinde alıntılanan prop yaklaşımının belgeleri olarak kendi kendine cevaplamak için bir soru yaratma cazibesi aldım. Ama bu soru tam olarak başlığım olurdu, bu yüzden yanıtı buraya ekledim. Belki yeni bir soru yaratmalıydım.
Ron C

9
@RonC Bu popüler bir soru ve cevabınız tam olarak birçok insanın aradığı şey, bu yüzden burada iyi oturduğunu düşünüyorum. Hepimizin sadece sınırlı bir zamanı var ve çoğumuz soruları zar zor okuduk, bu kesinlikle verdiğiniz cevaba bir eleştiri değildi, sadece orijinal cevabımın soru ve görüş birliği değildi. Aslında "bilgisayarlı izleme" yaklaşımını seviyorum, ancak birçok kişi cevabınızda kısa ve öz bir şekilde özetlediğiniz daha az kıvrımlı "tırnak" yaklaşımını tercih ediyor.
craig_h

10
Resmi belgeler şimdi bu yaklaşımı içerir
feihcsim

19

VueJ'ler alt nesnelerde derin izle

new Vue({
    el: "#myElement",
    data: {
        entity: {
            properties: []
        }
    },
    watch: {
        'entity.properties': {
            handler: function (after, before) {
                // Changes detected. Do work...     
            },
            deep: true
        }
    }
});

8

Bir mülkü bir süre izlemek ve ardından izini kaldırmak isterseniz nasıl olur?

Veya bir kütüphane alt bileşeni özelliğini izlemek için?

"Dinamik izleyici" yi kullanabilirsiniz:

this.$watch(
 'object.property', //what you want to watch
 (newVal, oldVal) => {
    //execute your code here
 }
)

$watchDenir eğer izlerken duracak bir unwatch işlevi döndürür.

var unwatch = vm.$watch('a', cb)
// later, teardown the watcher
unwatch()

Ayrıca şu deepseçeneği kullanabilirsiniz :

this.$watch(
'someObject', () => {
    //execute your code here
},
{ deep: true }
)

Lütfen dokümanlara bir göz attığınızdan emin olun


2
Dokümanlara göre , bir izleyici tanımlamak için ok işlevlerini kullanmamalısınız:Note that you should not use an arrow function to define a watcher (e.g. searchQuery: newValue => this.updateAutocomplete(newValue)). The reason is arrow functions bind the parent context, so this will not be the Vue instance as you expect and this.updateAutocomplete will be undefined.
wonder95

7

Değil kullanmak da mümkün burada bahsedilen görerek, ancak vue-property-decoratorsen uzanan eğer desen seninVue sınıfınızı .

import { Watch, Vue } from 'vue-property-decorator';

export default class SomeClass extends Vue {
   ...

   @Watch('item.someOtherProp')
   someOtherPropChange(newVal, oldVal) {
      // do something
   }

   ...
}

5

Ben bu çözümü 'kesmek' için kullanılan eklemek için başka bir yolu bunu yapmak oldu: Ben computedsadece iç içe nesne değerini döndürecek ayrı bir değer ayarladım .

data : function(){
    return {
        my_object : {
            my_deep_object : {
                my_value : "hello world";
            }.
        },
    };
},
computed : {
    helper_name : function(){
        return this.my_object.my_deep_object.my_value;
    },
},
watch : {
    helper_name : function(newVal, oldVal){
        // do this...
    }
}

iyi bir;) ama diziler ne olacak? :)
WEBCENTER

3

Kabul edilen cevap kullanımıyla ilgili sorunum, deep: truebir diziyi derinlemesine izlerken, dizinin hangi öğesinin değişikliği içerdiğini kolayca belirleyemememdir . Bulduğum tek net çözüm , her dizi öğesini ayrı ayrı izleyebilmeniz için bir bileşenin nasıl yapılacağını açıklayan bu cevaptır.


1
Evet bu doğru, ama derin bir gözlemcinin amacı bu değil (aslında bulacaksınız oldValvenewVal aynı nesneyi her ikisi de aynı nesneyi referans alır, aynıdır). Bir amacı deep watcherolan bir şey yapmak Mani'nin yanıtında sivri out gibi, vb Aksi takdirde, muhtemelen istediğiniz bir ajax arama bileşeni, bir olay EMIT gibi değerlerini değiştirmek, yapmak.
craig_h

Bireysel değişiklikleri takip etmekle aynı problemim vardı! Yine de bir çözüm buldum ve burada belgeledim .
Erik Koopmans

3

Bir listedeki değiştirilen öğeleri tek tek izleme

Bir listedeki tüm öğeleri izlemek ve listedeki hangi öğenin değiştiğini bilmek istiyorsanız, her öğeye ayrı ayrı özel gözlemciler kurabilirsiniz, örneğin:

var vm = new Vue({
  data: {
    list: [
      {name: 'obj1 to watch'},
      {name: 'obj2 to watch'},
    ],
  },
  methods: {
    handleChange (newVal, oldVal) {
      // Handle changes here!
      // NOTE: For mutated objects, newVal and oldVal will be identical.
      console.log(newVal);
    },
  },
  created () {
    this.list.forEach((val) => {
      this.$watch(() => val, this.handleChange, {deep: true});
    });
  },
});

Listeniz hemen doldurulmazsa (orijinal sorudaki gibi), mantığı createdgereken yere, örneğin .then()bloğun içine taşıyabilirsiniz .

Değişen bir listeyi izleme

Listenizin kendisi yeni veya kaldırılmış öğeler olacak şekilde güncellenirse, "sığ" listenin kendisini izleyen ve liste değiştikçe öğeleri dinamik olarak izleyen / izleyen yararlı bir model geliştirdim:

// NOTE: This example uses Lodash (_.differenceBy and _.pull) to compare lists
//       and remove list items. The same result could be achieved with lots of
//       list.indexOf(...) if you need to avoid external libraries.

var vm = new Vue({
  data: {
    list: [
      {name: 'obj1 to watch'},
      {name: 'obj2 to watch'},
    ],
    watchTracker: [],
  },
  methods: {
    handleChange (newVal, oldVal) {
      // Handle changes here!
      console.log(newVal);
    },
    updateWatchers () {
      // Helper function for comparing list items to the "watchTracker".
      const getItem = (val) => val.item || val;

      // Items that aren't already watched: watch and add to watched list.
      _.differenceBy(this.list, this.watchTracker, getItem).forEach((item) => {
        const unwatch = this.$watch(() => item, this.handleChange, {deep: true});
        this.watchTracker.push({ item: item, unwatch: unwatch });
        // Uncomment below if adding a new item to the list should count as a "change".
        // this.handleChange(item);
      });

      // Items that no longer exist: unwatch and remove from the watched list.
      _.differenceBy(this.watchTracker, this.list, getItem).forEach((watchObj) => {
        watchObj.unwatch();
        _.pull(this.watchTracker, watchObj);
        // Optionally add any further cleanup in here for when items are removed.
      });
    },
  },
  watch: {
    list () {
      return this.updateWatchers();
    },
  },
  created () {
    return this.updateWatchers();
  },
});

0

Cevapların hiçbiri işe yaramıyordu. Aslında, Bileşenler birden çok kez çağrıldığında iç içe verileri izlemek istiyorsanız. Bu yüzden onları tanımlamak için farklı sahne ile çağrılırlar. Örneğin<MyComponent chart="chart1"/> <MyComponent chart="chart2"/> Geçici çözümüm, en son güncellenen özelliğe işaret etmek üzere el ile güncelleştirdiğim bir addionnal vuex durum değişkeni oluşturmaktır.

İşte bir Vuex.ts uygulama örneği:

export default new Vuex.Store({
    state: {
        hovEpacTduList: {},  // a json of arrays to be shared by different components, 
                             // for example  hovEpacTduList["chart1"]=[2,6,9]
        hovEpacTduListChangeForChart: "chart1"  // to watch for latest update, 
                                                // here to access "chart1" update 
   },
   mutations: {
        setHovEpacTduList: (state, payload) => {
            state.hovEpacTduListChangeForChart = payload.chart // we will watch hovEpacTduListChangeForChart
            state.hovEpacTduList[payload.chart] = payload.list // instead of hovEpacTduList, which vuex cannot watch
        },
}

Mağazayı güncellemek için herhangi bir Bileşen işlevinde:

    const payload = {chart:"chart1", list: [4,6,3]}
    this.$store.commit('setHovEpacTduList', payload);

Şimdi güncellemeyi almak için herhangi bir Bileşende:

    computed: {
        hovEpacTduListChangeForChart() {
            return this.$store.state.hovEpacTduListChangeForChart;
        }
    },
    watch: {
        hovEpacTduListChangeForChart(chart) {
            if (chart === this.chart)  // the component was created with chart as a prop <MyComponent chart="chart1"/> 
                console.log("Update! for", chart, this.$store.state.hovEpacTduList[chart]);
        },
    },
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.