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-modal
到 follows
的任何事件或更改 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
,即使有推送,因为引用已更改为新实体。
当尝试设置您的初始 members
和 dataset
时,我建议使用一种初始化方法来创建您的 followList 的克隆并将其写入数据集,并且 运行组件生命周期的 created()
或 mounted()
钩子。然后为 members
创建一个计算 属性,不需要存储 followList.data 三次并且可能有数据集和成员分歧。
在JavaScript中,对象的属性也是对象本身,通过引用传递,而不是通过值传递。或者你可以说它们是 shallow copied
.
因此,在您的示例中,this.members
和 this.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));
}
我刚刚注意到一个意外行为,现在我不知道它是否正常。
我有一个名为 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-modal
到 follows
的任何事件或更改 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
,即使有推送,因为引用已更改为新实体。
当尝试设置您的初始 members
和 dataset
时,我建议使用一种初始化方法来创建您的 followList 的克隆并将其写入数据集,并且 运行组件生命周期的 created()
或 mounted()
钩子。然后为 members
创建一个计算 属性,不需要存储 followList.data 三次并且可能有数据集和成员分歧。
在JavaScript中,对象的属性也是对象本身,通过引用传递,而不是通过值传递。或者你可以说它们是 shallow copied
.
因此,在您的示例中,this.members
和 this.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));
}