【发布时间】:2020-04-13 15:06:28
【问题描述】:
我使用 Vue 已经有一段时间了,但仍然没有清楚地了解与这个(可能有争议的)问题相关的好处。我知道有人以不同的方式多次询问过这个问题,但我找不到明确的答案。
作为“示例 1”,假设我有一个包含 address 对象的父组件,并将该地址传递给子 AddressForm,以便用户可以编辑它,如下所示:
// Parent.vue
<template>
<AddressForm :address="address" @submit="onSubmit"/>
</template>
<script>
export default {
data () {
return {
address: {}
}
},
methods: {
onSubmit() {
console.log('Submit', this.address);
}
}
}
</script>
那么,子组件就长这样了,直接操作父地址对象的属性。
这里我只显示address.name 的输入以保持简洁,但您可以想象地址的任何其他属性也有类似的文本输入。
// AddressForm.vue
<template>
<form @submit.prevent="onSubmit">
<input v-model="address.name">
<button type="submit">Submit</button>
</form>
</template>
<script>
export default {
props: {
address: {
type: Object,
required: true,
}
},
methods: {
this.$emit('submit');
}
}
</script>
这里的问题是我直接在孩子中编辑address 属性。当然,我没有收到 Vue 的任何警告,因为我只是在编辑 address 对象的属性,实际上并没有通过重新分配来改变它。
有无数的地方我可以读到为什么这不是最佳实践,它是一种反模式,而且是一个坏主意。您不应该更改子组件中的道具。
好的,但是为什么呢?有人可以提供任何类型的真实世界用例,上面的代码可能会导致问题发生吗?
当我读到做事的“正确”替代方法时,我更加困惑,因为我没有看到任何真正的区别。这就是我的意思:
我们称之为“示例 2”,我现在使用 v-model 来强制执行“正确”的 2 路绑定,而不是像示例 1 中那样直接编辑道具:
// Parent.vue
<template>
<AddressForm v-model="address" @submit="onSubmit"/>
</template>
<script>
export default {
data () {
return {
address: {}
}
},
methods: {
onSubmit() {
console.log('Submit', this.address);
}
}
}
</script>
// AddressForm.vue
<template>
<form @submit.prevent="onSubmit">
<input v-model="localValue.name">
<button type="submit">Submit</button>
</form>
</template>
<script>
export default {
props: {
value: {
type: Object,
required: true,
}
},
computed: {
localValue: {
get: function() {
return this.value;
}
set: function(value) {
this.$emit('input', value);
}
}
},
methods: {
this.$emit('submit');
}
}
</script>
示例 1 和示例 2 有什么区别?他们在做的事情,从字面上看,完全一样吗?
在示例 2 中,计算属性表示 localValue 始终等于 value。意思是,它们是同一个对象,就像在示例 1 中一样。
此外,当您输入输入时,如果您看到在 Vue 调试器中触发的事件,AddressForm 甚至不会发出 input 事件,大概是因为 localValue 实际上没有被设置,因为它是只是一个正在更改的对象属性。
这再次表明示例 1 和示例 2 正在做完全相同的事情。即使使用了v-model,对象属性仍会在子级中直接发生变化。
所以,我的问题是,为什么示例 1 被认为是不好的做法?示例 2 有何不同?
【问题讨论】:
标签: vue.js