Vue 子组件双向绑定父组件数据对象内部多个属性,应该用什么方式实现?

2022-10-29 11:09:10 +08:00
 ignor

假设父组件数据对象 parentData 的结构为:

{
  a: xxx,
  b: yyy,
(后续会添加其他属性)
}

现在需要写一个可以编辑 parentData 的 component ,能像这样使用:

<EditorComponent v-model:parent-data="parentData"/>

在 EditorComponent 里,有两个 input 可以修改 a 、b 这两个属性。由于子组件不能直接修改父组件的数据,emit update 又只能对 parentData 做整块的赋值,所以目前的思路如下

先在 EditorComponent 内部复制一份 dataCopy:

data: {
  dataCopy = null
},
watch: {
  parentData: {
    deep: true,
    handler: function (val) {
      this.dataCopy = val
    }
  }
}

然后有两种处理方式:

1.
两个 input 绑定 dataCopy 的数据:

<input v-model="dataCopy.a"/>
<input v-model="dataCopy.b"/>

这样的话,如果要更新 parentData ,得在 dataCopy 上加一个 watcher ,然后

this.$emit('update:parentData', val)

但这似乎是不可取的,会造成来回无限更新

2.
把 input 的 v-model 拆开,加两个方法 updateA 、updateB:

<input :value="dataCopy.a" @input="updateA"/>
<input :value="dataCopy.b" @input="updateB"/>

然后在每个 update 里把 event 的值先赋给 dataCopy ,再

this.$emit('update:parentData', this.dataCopy)

这样属性一多就很麻烦很臃肿了

所以实现这样一个 EditorComponent 的规范思路应该是怎样的?(只看了基础教学就上手做了,不知道是否漏掉了什么重要概念……)

2679 次点击
所在节点    Vue.js
37 条回复
renmu
2022-10-29 11:17:39 +08:00
邪道:直接传 object ,然后子组件直接修改 object ,副作用也会使父组件的 object 变化。
如果你觉得过于邪道,还能子组件直接 watch object ,触发 emit ,这样就是 emit 了。

再正常一点就在子组件 deepcopy 一个 object ,然后再 watch
cydysm
2022-10-29 11:57:54 +08:00
对于你这个情况 其实你第二种可以使用 computed 了
localValue:{get( return moduleValue),set(){emit()}}
手机写的 看个大概吧
cydysm
2022-10-29 11:58:55 +08:00
#2 纠正下
localValue:{get(){return moduleValue}),set(){emit()}}
ChefIsAwesome
2022-10-29 12:02:25 +08:00
直接传子组件,让子组件改。
什么事件走来走去,或者是子组件复制一份数据,都瞎搞。罗里吧嗦,组件多了还严重影响性能。你琢磨琢磨如果这个子组件跟父组件写一个文件里,是不是就是直接改这个 data 。如果搞个什么 vuex ,是不是也就是直接改。
Carseason
2022-10-29 12:55:47 +08:00
provide/inject ?
zqx
2022-10-29 13:06:10 +08:00
只要不会复用,我永远不会新写一个组件。只要影响我晚下班的技术都不会用✔
ignor
2022-10-29 13:07:39 +08:00
@renmu copy object 再 watch ,第 1 点里提到了,copy object 会随原 object 更新,而 copy object 每次更新又会让原 object 更新,就死循环了
ignor
2022-10-29 13:10:01 +08:00
@cydysm computed 的确可以避免 1 里面的死循环,但这个 set 似乎不能 deep watch ,没办法感知 a 和 b 的 input 变化吧
ignor
2022-10-29 13:12:27 +08:00
@ChefIsAwesome 传子组件是什么操作能细说一下吗?查了下没找到相关文档
ignor
2022-10-29 13:14:00 +08:00
@Carseason 这好像是类似于全局变量的东西了吧…个人感觉应该慎用?
leadfast
2022-10-29 13:14:56 +08:00
是这个意思么?

```
<script setup lang="ts">
const props = defineProps({ parentData: { type: Object } });
const emit = defineEmits(['update:parentData']);
const myData = reactive({ ...props.parentData });
const updateFun = () => emit('update:parentData', myData);
</script>

<template>
<div>
<div><input type="text" v-model="myData.title" /></div>
<div><input type="text" v-model="myData.content" /></div>
<div><button @click="updateFun">update</button></div>
</div>
</template>
```
ignor
2022-10-29 13:16:27 +08:00
@zqx 哈哈,业余项目,解决问题手段固然很多,但就是想抱着学习的态度看看有没有好的实践方法

照理来说这也不是什么很复杂的需求,应该有比较标准的做法才对吧…
ignor
2022-10-29 13:23:30 +08:00
@leadfast 这个是手动更新了吧,我这边是想让 input 更新后自动把 parentData 更新
leadfast
2022-10-29 13:25:48 +08:00
@ignor #13 把事件放在 input 的 change 上?
leadfast
2022-10-29 13:37:14 +08:00
watch(myData, () => updateFun());
ignor
2022-10-29 13:37:16 +08:00
@leadfast 这里有个问题,就是不知道 change 调用 update 的时候,是在该 input 的 v-model 更新之前还是更新之后?如果是之前就赋不了新值了
ignor
2022-10-29 13:38:42 +08:00
@leadfast watch 就是第一点提到的,会死循环
leadfast
2022-10-29 13:47:23 +08:00
没有死循环

```
<script setup lang="ts">
import EditorComponent from './components/EditorComponent.vue';
const parentData = reactive({
title: 'Hello World',
content: 'This is a test',
});

const parentUpdate = (data: any) => {
console.log("mydata", JSON.stringify(data));
console.log("parentData-old", JSON.stringify(parentData));
Object.assign(parentData, data);
console.log("parentData-new", JSON.stringify(parentData));
};
</script>

<template>
<EditorComponent v-model:parent-data="parentData" @update:parent-data="parentUpdate" />
</template>
```
Manweill
2022-10-29 14:00:08 +08:00
ref 一把梭,属性传递我是怕了
Manweill
2022-10-29 14:00:45 +08:00
vue 这个不伦不类的东西,react 刚转来写 vue 各种不习惯

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/890909

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX