请教一下非原生组件的v-model传值问题?

<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修改值呢?

阅读 2.4k
3 个回答

emits('update:modelValue', data);的意思是触发update:modelValue事件通知父组件更新modelValue的值。所以按照同样的思路,我们可以在子组件中仅触发删除按钮的点击事件,在父组件中处理删除逻辑。

function handleDelete(index: number) {
  emits('delete', index);
}

在实际业务中,数组内容的修改、删除、添加一般也会涉及到其他逻辑,比如请求接口等。所以这样看来,子组件仅负责数据的展示,父组件负责相关的数据修改、业务功能等,也是合理的。

虽然你在做双向绑定,但是最好还是遵循单向数据流,数据修改后通知父组件这个数据修改了,子组件不要去直接改props.modelValue

// 通过索引删除元素
function handleDelete(index: number) {
  const data = props.modelValue.slice();
  data.splice(index, 1);
  emits('update:modelValue', data);
}

你这一段代码生效纯粹是因为数组是引用类型,传入之后 props.modelValue 和 data 都是引用地址而已,所以你改了 data,父组件的数据也改了

一般来说数据的双向绑定,会先将元数据深拷贝保存

const value = ref([])
watch(
    () => props.modelValue,
    (newVal) => {
        value.value = cloneDeep(newValue)
    }
)
function handleDelete(index: number) {
  value.value.splice(index, 1)
  emits('update:modelValue', value.value)
}
  1. 组件里很忌讳直接修改公共数据,基本上是万恶之源
  2. 所以第二种做法就不好
  3. vue 的优势不应该这么理解,Vue 的优势主要在于:只要你改了数据,就能改变视图。但是什么时候、该怎么改数据,仍然是我们要深思熟虑的。

故而这里通常应该是:

  • props.modelValue 克隆到本地,成为 localValue
  • 日常操作 localValue
  • 确认后,再提交给父组件
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题