[javascript] Vue-객체 배열을 깊이 관찰하고 변화를 계산합니까?

people다음과 같이 개체를 포함 하는 배열 이 있습니다.

전에

[
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 32},
  {id: 2, name: 'Joe', age: 38}
]

변경 될 수 있습니다.

[
  {id: 0, name: 'Bob', age: 27},
  {id: 1, name: 'Frank', age: 33},
  {id: 2, name: 'Joe', age: 38}
]

Frank가 방금 33 살이 된 것을보세요.

사람들 배열을 보려고하고 값이 변경되면 변경 사항을 기록하는 앱이 있습니다.

<style>
input {
  display: block;
}
</style>

<div id="app">
  <input type="text" v-for="(person, index) in people" v-model="people[index].age" />
</div>

<script>
new Vue({
  el: '#app',
  data: {
    people: [
      {id: 0, name: 'Bob', age: 27},
      {id: 1, name: 'Frank', age: 32},
      {id: 2, name: 'Joe', age: 38}
    ]
  },
  watch: {
    people: {
      handler: function (val, oldVal) {
        // Return the object that changed
        var changed = val.filter( function( p, idx ) {
          return Object.keys(p).some( function( prop ) {
            return p[prop] !== oldVal[idx][prop];
          })
        })
        // Log it
        console.log(changed)
      },
      deep: true
    }
  }
})
</script>

어제 어레이 비교에 대해 물어질문을 기반으로 가장 빠른 응답을 선택했습니다.

따라서이 시점에서 다음과 같은 결과를 기대합니다. { id: 1, name: 'Frank', age: 33 }

그러나 내가 콘솔로 돌아가는 것은 (구성 요소에 있음을 염두에두고) 다음과 같습니다.

[Vue warn]: Error in watcher "people"
(found in anonymous component - use the "name" option for better debugging messages.)

그리고 내가 만든 코드 펜 에서 결과는 내가 예상했던대로 변경된 변경된 객체가 아니라 빈 배열입니다.

왜 이런 일이 발생했는지 또는 내가 여기서 잘못되었는지 제안 할 수 있다면 크게 감사하겠습니다. 많은 감사합니다!



답변

이전 값과 새 값 간의 비교 기능에 문제가 있습니다. 나중에 디버깅 노력을 증가 시키므로 일을 너무 복잡하게하지 않는 것이 좋습니다. 간단하게 유지해야합니다.

가장 좋은 방법은 person-component아래와 같이 자체 구성 요소 내에서 모든 사람을 개별적 으로 만들고 관찰하는 것입니다.

<person-component :person="person" v-for="person in people"></person-component>

내부 사람 구성 요소를 관찰하는 작업 예제를 아래에서 찾으십시오. 부모 측에서 처리하려면 $emit을 사용 하여 id수정 된 사람을 포함하는 이벤트를 위쪽으로 보낼 수 있습니다 .

Vue.component('person-component', {
    props: ["person"],
    template: `
        <div class="person">
            {{person.name}}
            <input type='text' v-model='person.age'/>
        </div>`,
    watch: {
        person: {
            handler: function(newValue) {
                console.log("Person with ID:" + newValue.id + " modified")
                console.log("New age: " + newValue.age)
            },
            deep: true
        }
    }
});

new Vue({
    el: '#app',
    data: {
        people: [
          {id: 0, name: 'Bob', age: 27},
          {id: 1, name: 'Frank', age: 32},
          {id: 2, name: 'Joe', age: 38}
        ]
    }
});
<script src="https://unpkg.com/vue@2.1.5/dist/vue.js"></script>
<body>
    <div id="app">
        <p>List of people:</p>
        <person-component :person="person" v-for="person in people"></person-component>
    </div>
</body>


답변

문제를 해결하기 위해 구현을 변경했으며 이전 변경 사항을 추적하고이를 비교하는 개체를 만들었습니다. 문제를 해결하는 데 사용할 수 있습니다.

여기에서 이전 값이 별도의 변수에 저장되고 시계에서 사용되는 메서드를 만들었습니다.

new Vue({
  methods: {
    setValue: function() {
      this.$data.oldPeople = _.cloneDeep(this.$data.people);
    },
  },
  mounted() {
    this.setValue();
  },
  el: '#app',
  data: {
    people: [
      {id: 0, name: 'Bob', age: 27},
      {id: 1, name: 'Frank', age: 32},
      {id: 2, name: 'Joe', age: 38}
    ],
    oldPeople: []
  },
  watch: {
    people: {
      handler: function (after, before) {
        // Return the object that changed
        var vm = this;
        let changed = after.filter( function( p, idx ) {
          return Object.keys(p).some( function( prop ) {
            return p[prop] !== vm.$data.oldPeople[idx][prop];
          })
        })
        // Log it
        vm.setValue();
        console.log(changed)
      },
      deep: true,
    }
  }
})

업데이트 된 코드 펜보기


답변

잘 정의 된 행동입니다. 변경된 개체에 대한 이전 값을 가져올 수 없습니다 . newVal와 둘 다 oldVal동일한 객체를 참조하기 때문 입니다. Vue는 변경 한 개체의 이전 복사본을 유지 하지 않습니다 .

개체를 다른 개체 로 교체 했다면 Vue가 올바른 참조를 제공했을 것입니다.

문서Note섹션을 읽으십시오 . ( vm.$watch)

여기여기 에 대한 자세한 내용 .


답변

이것은 내가 물체를 깊이 관찰하는 데 사용하는 것입니다. 내 요구 사항은 개체의 자식 필드를 관찰하는 것이 었습니다.

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


답변

구성 요소 솔루션과 딥 클론 솔루션에는 장점이 있지만 다음과 같은 문제도 있습니다.

  1. 추상 데이터의 변경 사항을 추적하고 싶을 때가 있습니다. 해당 데이터를 중심으로 구성 요소를 구축하는 것이 항상 의미가있는 것은 아닙니다.

  2. 변경할 때마다 전체 데이터 구조를 딥 클로닝하는 것은 비용이 많이들 수 있습니다 .

더 나은 방법이 있다고 생각합니다. 당신이 목록에있는 모든 항목을보고 알고 싶은 경우에 있는 목록의 항목이 변경, 당신은 별도로 모든 항목에서 사용자 정의 전문가를 설정과 같이 할 수 있습니다 :

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

이 구조를 사용하면 handleChange()변경된 특정 목록 항목을 받게됩니다. 여기서 원하는대로 처리 할 수 ​​있습니다.

또한 목록에 항목을 추가 / 제거하는 경우 (이미있는 항목 만 조작하는 것이 아니라) 더 복잡한 시나리오를 여기에 문서화했습니다 .


답변