为什么Vue.js允许推送到prop数组?

时间:2019-06-14 10:57:09

标签: javascript vue.js

当我们尝试直接更改prop值时,Vue.js会显示警告,如下所示:

Vue.component('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 = [];
        }
    }
});

显示的警告是:

  

避免直接更改prop,因为每当父组件重新渲染时,该值都会被覆盖。而是使用基于属性值的数据或计算属性。

我知道为什么会发生这种情况,但是令我惊讶的是.push()不会发生这种情况。如果我更改了向数组添加值而不是重写值的方法,则不会发出警告:

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

为什么没有警告?

如何直接推动道具罚款而不能重写?

3 个答案:

答案 0 :(得分:3)

这仅仅是因为Array是一种参考内存。当您将ArrayObject存储在任何变量中时,它就是参考变量。

Memory management在这种情况下,引用变量将指向heap中的内存地址,因此您可以在地址中添加更多n个值。但是,您不能仅用任何新值替换该地址,即使使用新地址也是如此。

答案 1 :(得分:1)

被推入的道具是一个数组。推送新值后,它仍然是数组。我相信vue不会对开箱即用的道具进行深入的监视(即,它并不关心数组中的内容)。

引用this文章;

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]
     

这里的问题是:数组有变化吗?

     

好吧,这不是那么简单。

     

数组的内容已更改,但变量数组仍   指向相同的Array对象。数组容器没有改变,   但是数组内部发生了变化。

     

因此,当您观察数组或对象时,Vue不知道您已经   改变了道具里面的东西。您必须告诉Vue您想要它   在观察变化时检查道具内部。

Michael Thiessen的文章, 2018年10月发布-全部归功于他们。

针对您对此仍然是反模式的评论,我会这样说;

如果我们首先考虑为什么直接在组件上改变道具是一种反模式,那么推理仍然成立。再次引用迈克尔(我保证,我总是不小心绊倒他的东西);

  

“我们这样做是因为它确保每个组件都与   彼此。由此,我们可以保证一些可以帮助我们的事情   考虑我们的组件:只有组件可以更改自己的组件   州。只有组件的父项才能更改道具。”

答案 2 :(得分:1)

正如警告所说,这会导致意外行为

当组件被更新/渲染时,道具被作为值写入基础组件模型。这可以在“任何”时间再次发生。在v-if或v-for条件下渲染的组件通常会发生这种情况。在这种情况下,prop值再次从父级写入子级。

如果孩子增加了计数器,则只会增加道具的本地副本,原始值parent.data.counter仍将保持值5。如果更新了父项(例如,通过设置{{1} }),然后Vue将使用来自父级的新值覆盖子级中的值。因此,孩子的所有更改都丢失了-这可能导致意外行为。

如果prop是引用的话,这个问题就不会发生

如果prop是一个数组,则内存中仅存在该数组的单个副本。数组的任何突变都将更改父级和子级中的原始数组。如果您愿意,这仅适用于数组本身的突变。尝试counter=counter-1将会遇到与以前相同的问题。由于slice不会更改原始数组,而是创建一个副本,因此子级将与父级具有不同的引用,并且可能会表现出意外的行为。

这是一个演示问题的代码提要:

child.propArray = child.propArray.slice(3)
Vue.component('child-comp', {
  // 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) } }
})