admin管理员组

文章数量:1277394

I have this component code originally written in Vue 2 using the options API that is now being migrated to Vue 3. The code was simplified to the following where items are added/removed from the array and then the watcher is called to do other things (in this case just console.log):

<template>
  <button @click="addItem">Add</button>
  <button @click="removeItem">Remove</button>
  <br><br>
  <div>Items:</div>
  <div v-for="item in items">{{item}}</div>
</template>

<script>
export default {
  data() {
    return {
      items: [],
    };
  },
  watch: {
    items: {
      handler() {
        console.log('items', this.items);
      },
      //deep: true
    }
  },
  methods: {
    addItem() {
      this.items.push({i: this.items.length});
    },
    removeItem() {
      this.items.pop();
    }
  }
};
</script>

This worked fine in Vue 2 where the items watcher would fire each time an item is added/removed from the array. My actual object has far more attributes on it than just i, so if I mark the watcher with deep: true, the watcher fires as expected, but the view becomes unusably slow. How can I make this array.

I noticed if I change the addItem method to this.items = [...this.items, {i: this.items.length}];, then the watcher gets called as I would expect, but this also seems really inefficient since you're creating a new array each time. Is there another way to get the watcher to run only on push/pop/unshift/shift/splice calls that modify the existing array's structure without having to reassign the array reference or mark the array as deep?

I have this component code originally written in Vue 2 using the options API that is now being migrated to Vue 3. The code was simplified to the following where items are added/removed from the array and then the watcher is called to do other things (in this case just console.log):

<template>
  <button @click="addItem">Add</button>
  <button @click="removeItem">Remove</button>
  <br><br>
  <div>Items:</div>
  <div v-for="item in items">{{item}}</div>
</template>

<script>
export default {
  data() {
    return {
      items: [],
    };
  },
  watch: {
    items: {
      handler() {
        console.log('items', this.items);
      },
      //deep: true
    }
  },
  methods: {
    addItem() {
      this.items.push({i: this.items.length});
    },
    removeItem() {
      this.items.pop();
    }
  }
};
</script>

This worked fine in Vue 2 where the items watcher would fire each time an item is added/removed from the array. My actual object has far more attributes on it than just i, so if I mark the watcher with deep: true, the watcher fires as expected, but the view becomes unusably slow. How can I make this array.

I noticed if I change the addItem method to this.items = [...this.items, {i: this.items.length}];, then the watcher gets called as I would expect, but this also seems really inefficient since you're creating a new array each time. Is there another way to get the watcher to run only on push/pop/unshift/shift/splice calls that modify the existing array's structure without having to reassign the array reference or mark the array as deep?

Share Improve this question asked Feb 24 at 16:59 Kel VarnsenKel Varnsen 3541 gold badge2 silver badges9 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 2

Turns out as of Vue 3.5+, this can be fixed with deep watchers by marking the watcher with deep and then the level of depth to watch (in this case 1). So the watcher code becomes:

watch: {
  items: {
    handler() {
      console.log('items', this.items);
    },
    deep: 1
  }
},

This ensures the array watcher is called on updates to the structure of the array, but not deeply nested changes to objects within the array (much like how it worked in Vue 2).

The answer is the correct way to do this in Vue 3.5 and later. Prior to 3.5, this required an array to be iterated, which still may be much less expensive performance-wise than deep watcher:

created() {
  this.$watch(() => [...this.items], () => {
    console.log('items', this.items);
  });
}

本文标签: