图片
Vue 子组件修改props值常见的错误写法
在 Vue 中,props 应被视为只读的,直接修改 props 会导致难以追踪的 bug,并且违反了单向数据流的原则。
以下是一些常见的错误写法及其解释,以及正确的处理方法。
1. 常见错误写法
1.1. 直接修改 props
直接修改 props 是最常见的错误之一。
这种做法会导致 Vue 控制台输出警告,并且可能会导致不可预测的行为。
<template>
<div>
<p>{{ title }}</p>
<button @click="updateTitle">Update Title</button>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
title: String
});
const updateTitle = () => {
props.title = 'New Title'; // 错误:直接修改 props
};
</script>
1.2. 使用 v-model 但未正确处理
虽然 v-model 可以实现双向绑定,但如果使用不当,仍然会导致问题。
<template>
<div>
<p>{{ title }}</p>
<input v-model="title"> <!-- 错误:直接绑定到 props -->
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
title: String
});
</script>
1.3. 在 data 中直接使用 props
在 data 中直接使用 props 会导致数据的重复和不一致。
<template>
<div>
<p>{{ localTitle }}</p>
<button @click="updateTitle">Update Title</button>
</div>
</template>
<script setup>
import { ref, defineProps } from 'vue';
const props = defineProps({
title: String
});
const localTitle = ref(props.title); // 错误:未处理 props 的变化
const updateTitle = () => {
localTitle.value = 'New Title';
};
</script>
2. 正确的处理方法
2.1. 使用局部状态
在子组件中创建一个局部状态来存储 prop 的值,并在需要时更新这个局部状态。
使用 watch 来监听 prop 的变化。
<template>
<div>
<p>{{ localTitle }}</p>
<button @click="updateLocalTitle">Update Title</button>
</div>
</template>
<script setup>
import { ref, watch, defineProps } from 'vue';
const props = defineProps({
title: String
});
const localTitle = ref(props.title);
watch(() => props.title, (newVal) => {
localTitle.value = newVal;
});
const updateLocalTitle = () => {
localTitle.value = 'New Title';
};
</script>
2.2. 使用 $emit 发送事件
如果需要将子组件中的更改通知给父组件,可以使用 $emit 发送事件,让父组件来更新数据。
2.2.1. 子组件
<template>
<div>
<p>{{ title }}</p>
<button @click="updateTitle">Update Title</button>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
title: String
});
const emit = defineEmits(['update:title']);
const updateTitle = () => {
emit('update:title', 'New Title');
};
</script>
2.2.2. 父组件
<template>
<ChildComponent :title="title" @update:title="updateTitle" />
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const title = ref('Initial Title');
const updateTitle = (newTitle) => {
title.value = newTitle;
};
</script>
2.3. 使用计算属性
如果需要基于 props 的值进行一些计算,可以使用计算属性来实现。
<template>
<div>
<p>{{ computedTitle }}</p>
</div>
</template>
<script setup>
import { defineProps, computed } from 'vue';
const props = defineProps({
title: String
});
const computedTitle = computed(() => {
return `Modified: ${props.title}`;
});
</script>
2.4. 使用 v-model 或自定义模型
Vue 3 支持 v-model 的自定义修饰符,可以更方便地实现双向绑定。
2.4.1. 子组件
<template>
<div>
<p>{{ title }}</p>
<input v-model="localTitle" @input="onInput">
</div>
</template>
<script setup>
import { defineProps, defineEmits, computed } from 'vue';
const props = defineProps({
modelValue: String
});
const emit = defineEmits(['update:modelValue']);
const localTitle = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
});
const onInput = (event) => {
emit('update:modelValue', event.target.value);
};
</script>
2.4.2. 父组件
<template>
<ChildComponent v-model:title="title" />
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const title = ref('Initial Title');
</script>
3. 总结
- 直接修改 props:会导致 Vue 控制台警告和不可预测的行为。
- 使用 v-model 但未正确处理:可能导致数据绑定错误。
- 在 data 中直接使用 props:导致数据的重复和不一致。
通过使用局部状态、发送事件、计算属性或自定义模型,可以在不直接修改 props 的情况下,实现所需的功能,同时保持组件的可维护性和可预测性。