Vue.js-중첩 된 데이터를 올바르게 감시하는 방법


413

소품 변형을 올바르게 감시하는 방법을 이해하려고합니다. ajax 호출에서 데이터를 수신하고 데이터를 객체 안에 넣고 v-for 지시문을 통해 일부 하위 구성 요소를 렌더링 단순화 아래에 렌더링하는 상위 구성 요소 (.vue 파일)가 있습니다.

<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
@Reiner 이것은 그가 질문에서 피하려고했던 것이 었습니다. 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
이런 식으로 "핸들러"는 프롭에 변형이있을 때마다 호출됩니다. 제 목적은 변경 사항이 "prop"로 발생했는지 또는 "someOtherProp"의 myArray 내에서 개별적으로 변경이 발생했는지 관찰하기 위해 핸들러를 분리하는 것입니다.
플라스틱

9
특정 중첩 객체의 변경 사항을보고 싶다면 수행중인 작업이 좋습니다. 덜 어색한 구문을 원한다면 computed대신 jsfiddle.net/c52nda7x
craig_h

정확히 내가 찾던 것은 이제 나에게 너무나 논리적 인 것 같습니다. 중첩 된 소품을 반환하고 감시하는 계산 된 속성을 만듭니다. 많은 Tnx.
플라스틱

나는 두 번째 옵션이 원래 감시자와 크게 다르지 않다는 것을 추가하고 싶습니다. 내부적으로 계산 된 속성은 함수의 결과에 데이터 값을 설정하는 함수의 모든 참조 된 개체 / 값에 대한 감시자 일뿐입니다. 따라서 동일한 효과로 코드를 조금 더 복잡하게 만들 수 있습니다.
팔코

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그 자체가 매우 우아하지 않다고 주장 할 수는 있지만, 질문의 요점을 놓친 것입니다. 나는 대부분의 사람들이 이미 답을 포함하고있는 원래의 질문에 대해 글을 쓰고 있고 실제로 당신이 말한 것처럼 잘 문서화되지 않은 것을 찾고 있다고 생각합니다.
craig_h

1
@craig_h 매우 흥미로운 점. 나는 원래 중첩 된 소품을 보는 좋은 방법을 찾기 위해 몇 시간 동안 사냥을하고있었습니다. 그리고이 질문 제목은 내가 찾은 가장 가까운 질문이지만 질문 본문에 인용 된 소품을 나열했지만 답변이 그렇게 우아하지 않다는 것을 놓쳤습니다. 나는 다른 질문에 대한 의견에서 인용 된 소품 접근 방식을 마침내 발견했으며 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
docs 에 따르면 , 감시자를 정의하기 위해 화살표 기능을 사용해서는 안됩니다.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

여기에 언급되지 않았지만 수업을 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
예 사실이지만, 그 깊은 감시자가 (사실 당신이 찾을위한 게 아니에요 그 oldValnewVal두 참조 같은 객체, 그래서 동일). a의 의도 는 이벤트 생성, 아약스 호출 등과 같은 값이 변경 될 때 무언가deep watcher수행하는 것입니다 . 그렇지 않으면 Mani의 답변에서 지적한 것처럼 구성 요소를 원할 것입니다.
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"/> 내 해결 방법은 마지막으로 업데이트 된 속성을 가리 키도록 수동으로 업데이트하는 추가 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
        },
}

상점을 갱신하는 구성 요소 기능에서 :

    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.