ChatGPT解决这个技术问题 Extra ChatGPT

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

注意弗兰克刚满 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>

我基于关于数组比较的 question that I asked yesterday 并选择了最快的工作答案。

因此,此时我希望看到以下结果:{ id: 1, name: 'Frank', age: 33 }

但是我在控制台中得到的只是(记住我在一个组件中拥有它):

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

codepen that I made 中,结果是一个空数组,而不是我所期望的改变的对象。

如果有人能提出为什么会发生这种情况或我在这里出错的地方,那么将不胜感激,非常感谢!

我对以下所有解决方案的唯一问题是它们会在每次更改时触发,在我的情况下,我想进行数据库 API 调用以仅更新更改的项目。输入 v-model 将触发 100 次,我想在更改发生时记录更改,但在用户单击应用更改时将它们保存到数据库中,您将如何处理
@PirateApp 不确定我是否完全遵循您的问题,但也许这可以解决您遇到的问题:vuejs.org/v2/guide/forms.html#lazy。您还可以选择通过使用选择/更改事件来触发您的逻辑来更深入地控制发生的事情?没有例子很难说,用代码提出一个新问题并发布一个链接,我会尽力帮助解决它。
这个答案应该会有所帮助。您可以使用计算属性 stackoverflow.com/questions/52282586/… 直接查看年龄

M
Mani

您在旧值和新值之间的比较函数有一些问题。最好不要把事情复杂化,因为这会增加你以后的调试工作。你应该保持简单。

最好的方法是创建一个 person-component 并在其自己的组件中分别观察每个人,如下所示:

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

请在下面找到一个用于观看内部人员组件的工作示例。如果你想在父端处理它,你可以使用$emit向上发送一个事件,包含被修改的人的id

Vue.component('person-component', { props: ["person"], template: `

{{person.name}}
`, watch: { person: { handler: function(newValue) { console.log("ID:" + newValue.id + " modified") console.log("新时代: " + 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} ] } });

人员名单: p>


这确实是一个可行的解决方案,但并不完全符合我的用例。您会看到,实际上我有应用程序和一个组件,该组件使用 vue-material 表并列出具有在线编辑值的能力的数据。我正在尝试更改其中一个值,然后检查更改的内容,因此在这种情况下,它确实比较了前后数组以查看有什么区别。我可以实施您的解决方案来解决问题吗?事实上,我可能会这样做,但只是觉得它会与 vue-material 中这方面可用的内容的流动背道而驰
顺便说一句,感谢您花时间解释这一点,它帮助我了解了更多关于 Vue 的信息,我非常感激!
我花了一段时间才理解这一点,但你是绝对正确的,这就像一种魅力,如果你想避免混淆和进一步的问题,这是做事的正确方法:)
我也注意到了这一点并且有同样的想法,但是对象中还包含的是包含值的值索引,getter 和 setter 在那里,但相比之下它忽略了它们,因为缺乏更好的理解,我认为它确实如此不对任何原型进行评估。其他答案之一提供了它不起作用的原因,这是因为 newVal 和 oldVal 是同一件事,它有点复杂,但在一些地方已经解决了,而另一个答案提供了一个体面的工作,以便于创建用于比较目的的不可变对象。
但最终,您的方式更容易一目了然,并且在值变化时可用的内容方面提供了更大的灵活性。理解在 Vue 中保持简单的好处对我有很大帮助,但正如您在我的另一个问题中看到的那样,它有点卡住了。非常感谢! :)
F
Ferrybig

我已经更改了它的实现以解决您的问题,我创建了一个对象来跟踪旧更改并将其与之进行比较。您可以使用它来解决您的问题。

在这里,我创建了一个方法,其中旧值将存储在一个单独的变量中,然后将在手表中使用。

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

查看更新 codepen


因此,当它被挂载时,存储数据的副本并使用它来与它进行比较。有趣,但我的用例会更复杂,我不确定在从数组中添加和删除对象时它会如何工作,@Quirk 也提供了很好的链接来解决这个问题。但是我不知道你可以使用vm.$data,谢谢!
是的,我在手表之后也通过再次调用该方法来更新它,如果你回到原始值,那么它也会跟踪变化。
哦,我没有注意到隐藏在那里很有意义,并且是一种不太复杂的处理方式(与 github 上的解决方案相反)。
而且,如果您要从原始数组中添加或删除某些内容,只需再次调用该方法,您就可以再次使用该解决方案。
_.cloneDeep() 对我来说真的很有帮助。谢谢!!真的很有帮助!
t
tony19

这是定义明确的行为。您无法获取 mutated 对象的旧值。这是因为 newValoldVal 都引用同一个对象。 Vue 将不会保留您变异的对象的旧副本。

如果你用另一个对象替换了这个对象,Vue 会为你提供正确的引用。

阅读 docs. (vm.$watch) 中的 Note 部分

有关此 herehere 的更多信息。


哦,我的帽子,非常感谢!这是一个棘手的问题......我完全预计 val 和 oldVal 会有所不同,但在检查它们之后我发现它是新数组的两个副本,它之前没有跟踪它。多读一点,发现这个关于同样误解的未回答的 SO 问题:stackoverflow.com/questions/35991494/…
E
Erik Koopmans

组件方案和深度克隆方案各有优势,但也存在问题:

有时您想跟踪抽象数据的变化——围绕该数据构建组件并不总是有意义的。每次进行更改时深度克隆整个数据结构可能会非常昂贵。

我认为有更好的方法。如果您想查看列表中的所有项目并知道列表中的哪个项目发生了变化,您可以分别在每个项目上设置自定义观察者,如下所示:

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() 将接收已更改的特定列表项 - 从那里您可以进行任何您喜欢的处理。

我还记录了一个 more complex scenario here,以防您在列表中添加/删除项目(而不仅仅是操作已经存在的项目)。


谢谢 Erik,您提出了有效的观点,如果作为问题的解决方案实施,所提供的方法绝对是有用的。
在这种情况下,它只会跟随 created() 思想的变化吗?并且不是每次带有数组的对象的子属性都发生变化?我试过了,当我在创建的@ErikKoopmans 中添加它时,它没有将任何内容记录到控制台
@PA-GW created 中的代码只是设置了观察者。该代码运行后,只要列表中的任一值发生更改(如您设置 this.list[0].name = 'new name'),就会触发 handleChange 代码。
A
Alper Ebicoglu

这就是我用来深入观察物体的方法。我的要求是观察对象的子字段。

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

我相信您可能缺少对 stackoverflow.com/a/41136186/2110294 中描述的警告的理解。需要明确的是,这不是问题的解决方案,并且在某些情况下不会像您预期的那样起作用。
这正是我正在寻找的!谢谢
这里也一样,正是我需要的!谢谢。
这正是我正在寻找的!谢啦 ;)
M
Mohammadreza Khalili

如果我们有对象或对象数组,并且我们想在 Vuejs 或 NUXTJS 中观看它们,则需要在观看中使用 deep: true

    watch: {
      'Object.key': {
         handler (val) {
             console.log(val)
         },
         deep: true
       } 
     }

     watch: {
      array: {
         handler (val) {
             console.log(val)
         },
         deep: true
       } 
     }

m
menzel-m4m

而不是“看”,我用“计算”解决了这个问题!

我没有测试过这段代码,但我认为它应该可以工作。如果没有,请在评论中告诉我。

<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}
    ],
    oldVal: {},
    peopleComputed: computed({
      get(){
        this.$data.oldVal = { ...people };
        return people;
      },
      set(val){
        // Return the object that changed
        var changed = val.filter( function( p, idx ) {
          return Object.keys(p).some( function( prop ) {
            return p[prop] !== this.$data.oldVal[idx][prop];
          })
        })
        // Log it
        console.log(changed)
        this.$data.people = val;
      }
    }),
  }
})
</script>

您的答案可以通过额外的支持信息得到改进。请edit添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。您可以找到有关如何写出好答案的更多信息in the help center