Vue.js - วิธีดูข้อมูลที่ซ้อนกันอย่างเหมาะสม


413

ฉันพยายามที่จะเข้าใจวิธีการดูความแปรปรวนของเสา ฉันมีองค์ประกอบหลัก (ไฟล์. vue) ที่รับข้อมูลจากการโทร ajax วางข้อมูลไว้ในวัตถุและใช้เพื่อแสดงองค์ประกอบย่อยของเด็กผ่านทางคำสั่ง v-for ภายใต้การทำให้ง่ายขึ้นของฉัน:

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

... จากนั้นภายใน<script>แท็ก:

 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]);
            }
    });
}

วัตถุรายการเป็นเช่นนี้:

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

ตอนนี้ภายในองค์ประกอบ "ผู้เล่น" ลูกของฉันฉันกำลังพยายามดูการเปลี่ยนแปลงคุณสมบัติของรายการใด ๆ และฉันใช้:

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

มันใช้งานได้ แต่ดูเหมือนจะยุ่งยากเล็กน้อยสำหรับฉันและฉันสงสัยว่านี่เป็นวิธีที่ถูกต้องหรือไม่ เป้าหมายของฉันคือการดำเนินการบางอย่างทุกครั้งที่มีpropการเปลี่ยนแปลงหรือmyArrayรับองค์ประกอบใหม่หรือการเปลี่ยนแปลงบางอย่างภายในสิ่งที่มีอยู่ ข้อเสนอแนะใด ๆ จะได้รับการชื่นชม


9
เพียงใช้ "item.someOtherProp": function (newVal, oldVal){ตามที่ @Ron แนะนำ
Reiner

1
@ ผู้อ่านนี่เป็นสิ่งที่เขาพยายามหลีกเลี่ยงในคำถาม มันเป็นสิ่งเดียวกันโดยใช้ไวยากรณ์ ES5 จริงๆแล้วไม่มีค่าใช้จ่ายเพิ่มเติมในการดูการคำนวณและเป็นเทคนิคที่มีประโยชน์ในสถานการณ์ต่าง ๆ
craig_h

1
เป็นไปได้หรือไม่ที่จะเฝ้าสังเกตการเปลี่ยนแปลงkeysภายในวัตถุ? ตัวอย่าง:"item.*": function(val){...}
Charlie

คำตอบ:


622

คุณสามารถใช้เครื่องมือเฝ้าดูแบบลึกเพื่อ:

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

ตอนนี้จะตรวจจับการเปลี่ยนแปลงใด ๆ กับวัตถุในitemอาเรย์และส่วนเพิ่มเติมของอาเรย์เอง (เมื่อใช้กับVue.set ) นี่คือ JSFiddle: http://jsfiddle.net/je2rw3rs/

แก้ไข

หากคุณไม่ต้องการเฝ้าดูการเปลี่ยนแปลงทุกอย่างบนวัตถุระดับบนสุดและเพียงต้องการไวยากรณ์ที่น่าอึดอัดใจน้อยกว่าสำหรับการดูวัตถุที่ซ้อนกันโดยตรงคุณสามารถรับชมcomputedแทน:

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

นี่คือ JSFiddle: http://jsfiddle.net/oa07r5fw/


2
ด้วยวิธีนี้ "ตัวจัดการ" จะถูกเรียกเมื่อใดก็ตามที่เสามีการเปลี่ยนแปลงวัตถุประสงค์ของฉันคือการแยกตัวจัดการเพื่อที่จะสังเกตว่าการเปลี่ยนแปลงที่เกิดขึ้นกับ "เสา" หรือภายใน myArray ใน "someOtherProp" แยกต่างหาก
พลาสติก

9
หากคุณเพียงแค่ต้องการดูการเปลี่ยนแปลงของวัตถุที่ซ้อนกันเฉพาะสิ่งที่คุณกำลังทำอยู่ก็ดี หากคุณต้องการไวยากรณ์ที่น่าอึดอัดใจน้อยกว่าคุณสามารถรับชมได้ที่computed: jsfiddle.net/c52nda7x
craig_h

สิ่งที่ฉันกำลังมองหาตอนนี้ดูเหมือนว่าฉันมีเหตุผลเพื่อ: สร้างทรัพย์สินที่คำนวณได้ที่กลับเสาค้ำยันและดูมัน ขอบคุณมาก
พลาสติก

ฉันแค่อยากจะเพิ่ม - ว่าตัวเลือกที่สองนั้นไม่ได้แตกต่างจากนักดูดั้งเดิมมากนัก ภายในคุณสมบัติที่คำนวณได้เป็นเพียงผู้เฝ้าดูวัตถุ / ค่าอ้างอิงทั้งหมดในฟังก์ชั่นที่ตั้งค่าข้อมูลเป็นผลของฟังก์ชั่น - ดังนั้นคุณอาจสร้างรหัสที่ซับซ้อนขึ้นเล็กน้อยด้วยเอฟเฟกต์เดียวกัน
Falco

22
@Falco ฉันเพิ่งค้นพบคำตอบในความคิดเห็นที่นี่ peerbolte ระบุว่าสามารถทำได้โดยใส่ชื่อการดูในเครื่องหมายคำพูดเดี่ยว ฉันทดสอบสิ่งนี้แล้วและใช้งานได้ ดังนั้นในกรณีนี้มันจะwatch: { 'item.foo' : function(newVal, oldVal) { // do work here }} สวยลื่น ฉันรัก Vue!
Ron C

436

วิธีการที่ดีอีกวิธีหนึ่งและอีกวิธีที่สวยงามกว่านี้คือ:

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

(ฉันได้เรียนรู้วิธีการนี้จาก @peerbolte ในความคิดเห็นที่นี่ )


47
(ไม่ใช่แค่เพียงเล็กน้อย): P. สิ่งนี้ควรอยู่ในเอกสาร Vue.js เนื่องจากเป็นกรณีการใช้งานทั่วไป ฉันค้นหาวิธีแก้ปัญหาหลายชั่วโมงแล้วขอบคุณคำตอบของคุณ
Claudiu

11
@symba น่าสนใจที่ผู้คนไม่ได้สังเกตว่านี่เป็นสิ่งเดียวกันที่ OP ขอให้หลีกเลี่ยงเพียงแค่ในES5รูปแบบไวยากรณ์ แน่นอนว่าเราสามารถโต้แย้งได้ว่าES2015 method definitionตัวของมันเองนั้นไม่ได้สง่างามนัก แต่มันก็คิดถึงประเด็นของคำถามซึ่งเป็นวิธีที่จะหลีกเลี่ยงการล้อมชื่อได้ ฉันเดาว่าคนส่วนใหญ่กำลัง
เปล่งประกาย

1
@craig_h จุดที่น่าสนใจสุด ๆ เดิมทีฉันกำลังตามล่าหาเวลาหลายชั่วโมงเพื่อหาวิธีที่ดีในการดูเสาที่ซ้อน และชื่อคำถามนี้เป็นคำถามที่ใกล้เคียงที่สุดที่ฉันพบ แต่ฉันคิดถึงว่าเขาแสดงรายการอ้างอิงในเนื้อหาของคำถาม แต่พบคำตอบที่ไม่สวย ในที่สุดฉันก็พบวิธีการอ้างอิงที่ยกมาในความคิดเห็นของคำถามอื่นและถูกล่อลวงให้สร้างคำถามเพียงเพื่อตอบตัวเองเป็นเอกสารของวิธีการ prop ที่ยกมาใน SO แต่คำถามนี้คือสิ่งที่ชื่อของฉันจะเป็นดังนั้นฉันจึงเพิ่มคำตอบที่นี่ บางทีฉันควรสร้างคำถามใหม่
Ron C

9
@ RonC นี่เป็นคำถามยอดนิยมและคำตอบของคุณคือสิ่งที่ผู้คนจำนวนมากกำลังค้นหาดังนั้นฉันคิดว่ามันนั่งได้ดีที่นี่ พวกเราทุกคนมีเวลา จำกัด และพวกเราส่วนใหญ่แทบจะไม่อ่านคำถามดังนั้นนี่จึงไม่ใช่คำติชมของคำตอบที่คุณให้มาฉันแค่อยากจะชี้ให้เห็นว่าคำตอบดั้งเดิมของฉันนั้นเฉพาะกับ คำถามและไม่ได้ตั้งใจจะให้ความเห็น ฉันชอบวิธีการ "ดูที่คำนวณ" แต่หลายคนชอบวิธี "คำพูดที่ซับซ้อนน้อยกว่า" ซึ่งคุณได้สรุปโดยสังเขปในคำตอบของคุณ
craig_h

10
เอกสารอย่างเป็นทางการรวมถึงวิธีการนี้ในขณะนี้
feihcsim

19

VueJs จับตาดูวัตถุเด็ก

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

8

เป็นอย่างไรถ้าคุณต้องการดูคุณสมบัติสักพักหนึ่งแล้วจึงเลิกดู

หรือดูคุณสมบัติองค์ประกอบลูกไลบรารี?

คุณสามารถใช้ "ผู้เฝ้าดูแบบไดนามิก":

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

$watchส่งกลับฟังก์ชันเฝ้าซึ่งจะหยุดดูถ้ามันถูกเรียกว่า

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

นอกจากนี้คุณสามารถใช้deepตัวเลือก:

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

โปรดตรวจสอบเอกสาร


2
ตามเอกสารคุณไม่ควรใช้ฟังก์ชั่นลูกศรเพื่อกำหนดผู้เฝ้าดู: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.
สงสัย 95

7

ไม่เห็นว่ามีการกล่าวถึงที่นี่ แต่ยังสามารถใช้vue-property-decoratorรูปแบบได้หากคุณขยายVueชั้นเรียน

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

export default class SomeClass extends Vue {
   ...

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

   ...
}

5

อีกวิธีในการเพิ่มที่ฉันใช้ในการ 'แฮ็ค' โซลูชันนี้คือการทำสิ่งนี้: ฉันตั้งค่าแยกต่างหากcomputedที่จะคืนค่าออบเจ็กต์ซ้อน

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

ดีหนึ่ง;) แต่มีอาร์เรย์อะไรบ้าง :)
WEBCENTER

3

ปัญหาของฉันกับคำตอบที่ได้รับการยอมรับในการใช้deep: trueคือเมื่อลึกดูอาร์เรย์ฉันไม่สามารถระบุได้อย่างง่ายดายซึ่งองค์ประกอบของอาร์เรย์มีการเปลี่ยนแปลง คำตอบที่ชัดเจนที่ฉันพบคือคำตอบนี้ซึ่งอธิบายถึงวิธีการสร้างส่วนประกอบเพื่อให้คุณสามารถดูแต่ละองค์ประกอบของอาร์เรย์ได้ทีละรายการ


1
ใช่มันเป็นเรื่องจริง แต่นั่นไม่ใช่สิ่งที่นักสังเกตการณ์ลึกไว้สำหรับ (อันที่จริงแล้วคุณจะพบoldValและnewValทั้งคู่อ้างถึงวัตถุเดียวกันดังนั้นก็เหมือนกัน) ความตั้งใจของ a deep watcherคือการทำบางสิ่งบางอย่างเมื่อค่าเปลี่ยนแปลงเช่นส่งเหตุการณ์โทร ajax ฯลฯ มิฉะนั้นคุณอาจต้องการส่วนประกอบดังที่ระบุไว้ในคำตอบของมณี
craig_h

ฉันมีปัญหาแบบเดียวกันกับการติดตามการเปลี่ยนแปลงรายบุคคล! ผมพบว่าวิธีการแก้ปัญหาแม้ว่าและเอกสารมันนี่
Erik Koopmans

3

ติดตามรายการที่เปลี่ยนแปลงแต่ละรายการในรายการ

หากคุณต้องการที่จะดูรายการทั้งหมดในรายการและรู้ซึ่งรายการในรายการการเปลี่ยนแปลงคุณสามารถตั้งค่าดูที่กำหนดเองในทุกรายการแยกต่างหากเช่นดังนั้น:

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});
    });
  },
});

หากรายการของคุณไม่ได้บรรจุโดยตรง (เช่นในคำถามเดิม) คุณสามารถย้ายตรรกะออกcreatedไปยังที่ที่ต้องการเช่นใน.then()บล็อก

ดูรายการเปลี่ยนแปลง

หากรายการของคุณอัปเดตให้มีรายการใหม่หรือลบออกฉันได้พัฒนารูปแบบที่มีประโยชน์ที่ "ตื้น" เฝ้าดูรายการเองและเฝ้าดู / ปลดล็อครายการแบบไดนามิกเมื่อรายการเปลี่ยนแปลง

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

ไม่มีคำตอบสำหรับฉันที่ทำงาน จริง ๆ แล้วถ้าคุณต้องการดูข้อมูลซ้อนกับคอมโพเนนต์ที่เรียกว่าหลายครั้ง ดังนั้นพวกเขาจึงถูกเรียกพร้อมกับอุปกรณ์ประกอบฉากที่แตกต่างกันเพื่อระบุพวกเขา ตัวอย่างเช่น<MyComponent chart="chart1"/> <MyComponent chart="chart2"/> วิธีแก้ปัญหาของฉันคือการสร้างตัวแปรสถานะ addionnal vuex ที่ฉันอัพเดตด้วยตนเองเพื่อชี้ไปยังคุณสมบัติที่อัพเดตล่าสุด

นี่คือตัวอย่างการใช้ Vuex.ts:

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

ในฟังก์ชัน Component ใด ๆ เพื่ออัปเดตร้านค้า:

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

ในส่วนใดก็ได้เพื่อรับการอัปเดต:

    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]);
        },
    },
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.