Warning on mutation of string but not nested object?

I’ve just begun using Vue (2.1.3) and notice what seems to be inconsistent behavior concerning warnings as the result of prop mutations. I’m still unfamiliar with many details of Vue but I haven’t seen this specifically addressed in the documentation (though I could have missed something somewhere). Please see a very simple illustration below.

HTML:

<div id="main">
    <x-comp :comp1="val1" :comp2="val2"></x-comp>
</div>

Component:

Vue.component("x-comp",
{
    props:
    {
        comp1: String,
        comp2: Object
    },
    template: `<div>{{ comp1 }} | {{ comp2.val3 }}
               <button @click="act">Act</button></div>`,
    methods:
    {
        act: function()
        {
            this.comp1 = "newblah1";
            this.comp2.val3 = "newblah2";
        }
    }
});

Instance:

var vm = new Vue({
    el: "#main",
    data:
    {
        val1: "blah1",
        val2:
        {
            val3: "blah2"
        }
    }
});

The HTML displays the values of val1 and val3; the component simply intends to replace the “blah” text with the “newblah” version. When the Act button is clicked both do change on the page. But Vue issues the following warning:

[Vue warn]: Avoid mutating a prop directly since the value will be
overwritten whenever the parent component re-renders. Instead,
use a data or computed property based on the prop's value. Prop
being mutated: "comp1" (found in component <x-comp>)

It doesn’t seem to have a problem with the local mutation of the child element of the val2/comp2 object, though. Is there a reason for the omission or is this just an oversight in Vue’s warning system?

We should not be mutating the props locally either way. You could add a data option to the component:

data: function()
{
    return {
        localcomp1: this.comp1,
        localcomp2: this.comp2
    }
},

Now the act method results in the same warning and actually fails to update comp1. Isn’t the point of a data function local to the component to create a separate scope unattached to the parent? If so why does localcomp2 allow the update?

Any information would be much appreciated. Thank you.

The reason that your localcomp2, is still updating comp2 (from the parent) is because objects are passed around as references. So you update the same object even though 2 variables point to it. To avoid that, you would need to clone the object, either using a library such as lodash, or object.assign({}, this.comp2) (although I believe object.assign is only a shallow copy and not deep)

I hadn’t remembered Object.assign() and similar but that does make sense. The discrepancies noted earlier still apply, though: if both localcomp* variables are just pointing to the instance’s comp* variables then why would the results of act be different? (localcomp1 doesn’t update the <div> but localcomp2 does.)

Less importantly: if you follow through (as I neglected to do in my original post) and keep the localcomp reference assignments but change the act method from

this.comp1 = "newstring1";
this.comp2.val3 = "newstring2";

to

this.localcomp1 = "newstring1";
this.localcomp2.val3 = "newstring2";

then you still get only the “newstring2” change in the <div> but you don’t get a warning. That kind of sounds like it could be a simple gap in the warning checks, though; perhaps it’s related to a difference in the way that JavaScript assigns primitives and objects.

Thanks; I would like to hear some responses to the above but will be experimenting further with intercomponent communication.

I think this is because of primitives vs objects. Vue can detect the attempts to modify a primitive inside the child component, but cant on an object (or the reference of it) hence why you can modify the object (val2/localComp2) in your example, but modifying comp1 throws an error (since it is a primitive on that child component). And actually, both localcomp* variables are not pointing to the parent ones, only the object is (localComp2). LocalComp1 is not passed by reference (since it is a string) hence why updating it in the component will not affect the parent, however, since Vue is 1 way data flow, it throws you this warning because while if you do change the prop value directly, if the parent updates on its next render, the child will be refreshed with possibly the original overriding your previous mutation of it in the child component. Something you dont want, or can easily predict.

This is my understanding, I do not believe there is any oversight in vues warning.