<template>
<ElTag
v-for="(item,i) in modelValue"
:key="item"
closable
@close="handleDelete(i)"
>
{{ item }}
</ElTag>
</template>
<script setup lang="ts">
import { ElInput, ElTag } from "element-plus";
const props = withDefaults(defineProps<{
modelValue?: string[];
}>(),{
modelValue: () => [],
});
const emits = defineEmits(["update:modelValue"]);
// 通过索引删除元素
function handleDelete(index: number) {
const data = props.modelValue.slice();
data.splice(index, 1);
emits('update:modelValue', data);
}
</script>
在上面的代码中,handlerDelete
是将props.modelValue
的原数组通过slice
进行浅克隆,最后将浅克隆的值传回父组件,通过父组件修改原值,之所以使用这种传值方式,是因为我采用了react
的更新机制。
然而由于vue
本身是响应式数据,因此直接修改props.modelValue
也能实现:
function handleDelete(index: number) {
// 这里甚至都不用emit事件
props.modelValue.splice(index, 1);
}
问题
因此这引发了我的一个思考:
- 第二种直接修改props的方法虽然方便,但是带来了许多的隐患,组件内部随意更改外部值,会导致数据流紊乱,会导致许多的后期问题,因此不可取。
- 但是第一种浅克隆的方式又丢掉了
vue
响应式的优势。
所以:我想问下有没有更优雅的方式实现v-model
修改值呢?
emits('update:modelValue', data);
的意思是触发update:modelValue
事件通知父组件更新modelValue
的值。所以按照同样的思路,我们可以在子组件中仅触发删除按钮的点击事件,在父组件中处理删除逻辑。在实际业务中,数组内容的修改、删除、添加一般也会涉及到其他逻辑,比如请求接口等。所以这样看来,子组件仅负责数据的展示,父组件负责相关的数据修改、业务功能等,也是合理的。