最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why does Vue.js allow pushing to prop array? - Stack Overflow

programmeradmin1浏览0评论

Vue.js displays warning when we try to change the prop value directly, like this:

Vueponent('Games', {
    template: `
        <div>
            <ol>
                <li v-for="game in games">{{ game }}</li>
            </ol>
            <button @click="clear">Clear games</button>
        </div>
    `,
    props: ['games'],
    methods: {
        clear: function () {
            this.games = [];
        }
    }
});

The warning being displayed is:

Avoid mutating a prop directly since the value will be overwritten whenever the parent ponent re-renders. Instead, use a data or puted property based on the prop's value.

I know why that happens, but what I find surprising is that it doesn't happen with .push(). If I change the method to add a value to the array instead of rewriting it, there's no warning:

methods: {
    add: function () {
        this.games.push('Whatever');
    }
}

Why is there no warning? How is pushing directly to the prop fine and rewriting is not?

Vue.js displays warning when we try to change the prop value directly, like this:

Vue.ponent('Games', {
    template: `
        <div>
            <ol>
                <li v-for="game in games">{{ game }}</li>
            </ol>
            <button @click="clear">Clear games</button>
        </div>
    `,
    props: ['games'],
    methods: {
        clear: function () {
            this.games = [];
        }
    }
});

The warning being displayed is:

Avoid mutating a prop directly since the value will be overwritten whenever the parent ponent re-renders. Instead, use a data or puted property based on the prop's value.

I know why that happens, but what I find surprising is that it doesn't happen with .push(). If I change the method to add a value to the array instead of rewriting it, there's no warning:

methods: {
    add: function () {
        this.games.push('Whatever');
    }
}

Why is there no warning? How is pushing directly to the prop fine and rewriting is not?

Share Improve this question asked Jun 14, 2019 at 10:57 Robo RobokRobo Robok 22.9k20 gold badges83 silver badges144 bronze badges 3
  • The prop being pushed to is an array. After pushing a new value, it is still an array. I believe vue doesn't do a deep watch on props out of the box (i.e, it doesn't care about what's inside the array). – Someone Commented Jun 14, 2019 at 10:59
  • 1 How does it matter that it's of the same type? Vue.js even has its own wrapper for Array.push from what I know, so it would make things even easier to control. Changing the props is said to be wrong in case of re-rendering. It's not about the type, but about the value being different than a value being passed in a prop. – Robo Robok Commented Jun 14, 2019 at 11:03
  • I'll post a more in depth answer now. – Someone Commented Jun 14, 2019 at 11:03
Add a ment  | 

3 Answers 3

Reset to default 4

It's just because Array is kind of reference memory. When you have Array or Object stored in any variable it's a reference variable.

Memory management in such case, The reference variable will points to a memory address in heap so you can add more n more value to address. However you cannot just replace that address with any new value even with the new address.

As the warning says, one results in unexpected behaviour

When a ponent is updated/rendered, the props are written as values to the underlying ponent model. This can happen again at "any" time. This will often happen with ponents rendered in v-if or v-for conditions. In this case the prop value is written again from the parent to the child.

If the child increases the counter it will only increase it's local copy of the prop, the original value parent.data.counter will still remain at the value 5. If the parent is updated (for example by setting counter=counter-1) then Vue will override the value in the child with the new value from the parent. So all changes of the child are lost - and this can lead to unexpected behaviour.

This problem will not happen, if the prop is a reference, which is mutated

If the prop is an array, there exists only a single copy of this array in memory. Any mutation of the array will change the original array in parent as well as in child. This will only work for mutations of the array itself, if you would e.g. try child.propArray = child.propArray.slice(3) you will have the same problems as before. Since slice does not change the original array, but creates a copy the child will have a different reference than the parent and unexpected behaviour will likely manifest.

Here is a code-fiddle demonstrating the issues:

Vue.ponent('child-p', {
  // camelCase in JavaScript
  props: ['counter', 'arr'],
  template: `<h3>Child Counter: {{ counter }} <button @click="inc">+</button>
     -- Array: {{arr}} <button @click="push">push</button></h3></h3>`,
  methods: { inc() { this.counter++ }, push() { this.arr.push(this.arr.length+1) } }
})

new Vue({
  el: '#main',
  data: { counter: 1, arr: [] },
  methods: { inc() { this.counter++ }, push() { this.arr.push(this.arr.length+1) } }
})
<script src="https://cdnjs.cloudflare./ajax/libs/vue/2.5.17/vue.js"></script>
<main id="main">
   <h3>Main Counter: {{counter}} <button @click="inc">+</button>
     -- Array: {{arr}} <button @click="push">push</button></h3>
   <child-p :counter="counter" :arr="arr"></child-p>
   <child-p :counter="counter" :arr="arr"></child-p>
</main>

The prop being pushed to is an array. After pushing a new value, it is still an array. I believe vue doesn't do a deep watch on props out of the box (i.e, it doesn't care about what's inside the array).

To quote this article;

const array = [1, 2, 3, 4];
// array = [1, 2, 3, 4]

Now you update the array by pushing some more values into it:

array.push(5);
array.push(6);
array.push(7);
// array = [1, 2, 3, 4, 5, 6, 7]

Here's the question: has array changed?

Well, it's not that simple.

The contents of array have changed, but the variable array still points to the same Array object. The array container hasn't changed, but what is inside of the array has changed.

So when you watch an array or an object, Vue has no idea that you've changed what's inside that prop. You have to tell Vue that you want it to inspect inside of the prop when watching for changes.

Article By Michael Thiessen, Posted Oct 2018 - All credit to them.

In response to your ment about this still being an anti-pattern, I'd say this;

If we think about why mutating props on a ponent directly is an anti-pattern in the first place, the reasoning still stands. To quote Michael again (I keep stumbling across his stuff by accident, I promise);

"We do this because it ensures that each ponent is isolated from each other. From this we can guarantee a few things that help us in thinking about our ponents: Only the ponent can change it's own state. Only the parent of the ponent can change the props."

发布评论

评论列表(0)

  1. 暂无评论