vue 来自道具的意外反应

vue unexpected reactivity from props

我刚刚注意到一个意外行为,现在我不知道它是否正常。

我有一个名为 follows 的组件和一个名为 follow-list-modal

的子组件

我正在将 followList(分页)从 follows 传递到其子组件 follow-list-modal

follow-list-modal 中,我将分页数组存储在变量 members

Follows.vue
<template>
   <div>
         <follow-list-modal
           :follow-list="dataset">
          </follow-list-modal>
  
   </div>
</template>
<script>
export default {
props: {
   dataset: {
      type: Object,
      default: {},
    },
  },
}
</script>
FollowListModal.vue
<template>
   <div>
      <button @click="fetchMore"> More </button>
   </div>
</template>
<script>
export default {
props: {
   followList: {
      type: Object,
      default: {},
    },
data() {
    return {
      members: this.followList.data,
      dataset: this.followList,
    };
  },
methods: {
    fetchMore() {
            let nextPage = parseInt(this.dataset.current_page)  + 1;
            axios
              .get(this.dataset.path + '?page=' + nextPage)
              .then(({ data }) => this.refresh(data))
              .catch((error) => console.log(error));
           }
},
refresh(paginatedCollection) {
      this.dataset = paginatedCollection;
      this.members = this.members.concat(...paginatedCollection.data);
    },
}

当我单击 follow-list-modal 中的按钮 More 以获取更多数据时,我想将新数据附加到 members 数组。

意外行为(至少对我而言)。是如果我在 refresh 方法

中使用 push
this.members.push(..paginatedCollection.data); 

它不仅将数据 附加到 members ,而且还将 附加到 followList,这是来自父组件 follows

但是如果我使用 concat 而不是 push,它只会将数据附加到 members 变量而不是 followList

this.members = this.members.concat(..paginatedCollection.data);

这种行为正常吗?

我不明白为什么当 members 变量改变时 followList 改变,我认为反应性是一种方式。

换句话说,当 followList 改变时 members 改变,而不是相反

P.S 我不发出从 follow-list-modalfollows 的任何事件或更改 follows 的数据follow-list-modal

中任何方式的组件

您使用直接 link 到您的道具的(最初未定义的)属性 来实例化您的数据。这个 属性 是一个像对象一样的复杂实体(数组是对象),因此通过引用来调用。由于 members 在内存中引用与 followList.data 相同的内容,因此当您调用 members 时,它将遵循对与 followList.data 相同实体的引用。这与 Vue2 反应性无关,but here's a link nontheless.

  • push 改变调用它的数组;它将通过 members 跟随引用并更改 followList.data,并在通过 followList 调用时更新其值。因为 data 键在组件实例化时不存在,Vue 无法观察它(就像你需要使用 Vue.set 向数据对象添加新键时)。
  • concat returns一个新的合并元素数组,然后替换 members 中对新数组的引用。因此从现在开始你会 不再变异 followList.data,即使有推送,因为引用已更改为新实体。

当尝试设置您的初始 membersdataset 时,我建议使用一种初始化方法来创建您的 followList 的克隆并将其写入数据集,并且 运行组件生命周期的 created()mounted() 钩子。然后为 members 创建一个计算 属性,不需要存储 followList.data 三次并且可能有数据集和成员分歧。

在JavaScript中,对象的属性也是对象本身,通过引用传递,而不是通过值传递。或者你可以说它们是 shallow copied.

因此,在您的示例中,this.membersthis.followList.data 指向内存中的同一个变量。
所以,如果你改变 this.members,它也会改变 this.followList.data

您可以通过对对象进行深拷贝来避免这种情况。最简单,也可以说是最快的方法是使用 JSON.parse(JSON.stringify(obj)),但是 look at this answer for more examples.

data() {
    return {
      members: [],
      dataset: [],
    };
},
created() {
    this.members = JSON.parse(JSON.stringify(this.followList.data));
    this.dataset = JSON.parse(JSON.stringify(this.followList));
}