假设父组件数据对象 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 的规范思路应该是怎样的?(只看了基础教学就上手做了,不知道是否漏掉了什么重要概念……)
1
renmu 2022-10-29 11:17:39 +08:00 via Android
邪道:直接传 object ,然后子组件直接修改 object ,副作用也会使父组件的 object 变化。
如果你觉得过于邪道,还能子组件直接 watch object ,触发 emit ,这样就是 emit 了。 再正常一点就在子组件 deepcopy 一个 object ,然后再 watch |
2
cydysm 2022-10-29 11:57:54 +08:00 via iPhone
对于你这个情况 其实你第二种可以使用 computed 了
localValue:{get( return moduleValue),set(){emit()}} 手机写的 看个大概吧 |
3
cydysm 2022-10-29 11:58:55 +08:00 via iPhone
#2 纠正下
localValue:{get(){return moduleValue}),set(){emit()}} |
4
ChefIsAwesome 2022-10-29 12:02:25 +08:00
直接传子组件,让子组件改。
什么事件走来走去,或者是子组件复制一份数据,都瞎搞。罗里吧嗦,组件多了还严重影响性能。你琢磨琢磨如果这个子组件跟父组件写一个文件里,是不是就是直接改这个 data 。如果搞个什么 vuex ,是不是也就是直接改。 |
5
Carseason 2022-10-29 12:55:47 +08:00
provide/inject ?
|
6
zqx 2022-10-29 13:06:10 +08:00 via Android
只要不会复用,我永远不会新写一个组件。只要影响我晚下班的技术都不会用✔
|
7
ignor OP @renmu copy object 再 watch ,第 1 点里提到了,copy object 会随原 object 更新,而 copy object 每次更新又会让原 object 更新,就死循环了
|
8
ignor OP @cydysm computed 的确可以避免 1 里面的死循环,但这个 set 似乎不能 deep watch ,没办法感知 a 和 b 的 input 变化吧
|
9
ignor OP @ChefIsAwesome 传子组件是什么操作能细说一下吗?查了下没找到相关文档
|
11
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> ``` |
12
ignor OP |
13
ignor OP @leadfast 这个是手动更新了吧,我这边是想让 input 更新后自动把 parentData 更新
|
15
leadfast 2022-10-29 13:37:14 +08:00
watch(myData, () => updateFun());
|
16
ignor OP @leadfast 这里有个问题,就是不知道 change 调用 update 的时候,是在该 input 的 v-model 更新之前还是更新之后?如果是之前就赋不了新值了
|
18
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> ``` |
19
Manweill 2022-10-29 14:00:08 +08:00
ref 一把梭,属性传递我是怕了
|
20
Manweill 2022-10-29 14:00:45 +08:00
vue 这个不伦不类的东西,react 刚转来写 vue 各种不习惯
|
21
cuicuiv5 2022-10-29 14:06:54 +08:00
用 vuex
|
22
ignor OP @leadfast 你这里 myData=props.parent ,似乎不会对 parent 的变化做出响应,所以没有死循环,得用两个 watcher 才能实现双向的响应吧
|
23
ignor OP @Manweill 第一次尝试响应式框架,为了好上手选了 Vue ,所以这是……踩了坑了? react 是怎么规避这种问题的呢?
|
26
ignor OP @ignor #22 刚才又试了下,原来是因为在 watcher 里对新值做了改动才导致死循环……没事了
|
28
Garwih 2022-10-29 15:15:04 +08:00
正确的方法是用 computed ,get 的时候 return parentData ,set 的时候 this.$emit('update:parentData', value)。
并不需要 watch 。 |
29
dog82 2022-10-29 15:56:31 +08:00
我建议 vuex
|
30
ShayneWang 2022-10-29 16:01:16 +08:00
|
31
umaker 2022-10-29 16:30:08 +08:00 1
1. 使用 slot 实现
2. 使用一个状态管理工具 为了做到不直接修改对象类型的 prop ,搞一堆 emit 和 watch ,我觉得挺闹心的。 |
32
vanillacloud 2022-10-29 20:57:32 +08:00 via iPhone
是否会觉得你的 model 在设计上就跟 Vue 的理念有所不同,导致做起来觉得各种麻烦?
我觉得思考一下「 Vue 建议怎么做这样的事」比较好。就像刚才你问「 react 怎么处理这类问题」,别人会说「 react 一般单向,没这个问题」。 有没有可能 Vue 其实也是这个答案? 我不是 expert ,但一般做的时候都是 #28 @Garwih 那样的处理方式。这似乎也是 Vue document 的方法。有什么情况是不能这样解决的吗? 当你觉得你的情况很复杂的时候,是时候回头想一想 data model / structure 的设计。 |
33
ignor OP @vanillacloud #28 的问题我在#8 已经提到了,主楼的问题在#26 也解决了,主楼里方案 1 的做法其实可行,是我在 watcher 里加了赋值导致的死循环
|
34
kevin1 2022-10-29 21:57:35 +08:00
子组件维护自己的 input ,change 事件触发的时候拿到 value ,$emit 通知父组件(传递要修改的 key 和 value )直接修改 parentData 对应的 key ?
|
35
ignor OP @kevin1 我帖子里没提到,a b 这些属性内部的结构也是不确定的,例如 a 可能是由数组构成的一串 input ,你的这个办法似乎需要针对不同的结构写不同的 emit
|
36
cjd6568358 2022-10-30 01:34:34 +08:00
v-bind.sync?
|
37
encro 2022-10-30 09:37:15 +08:00
找到现实中一个类似的功能,然后看它怎么实现的,研究 2-3 个后,思考下他们的适用场景,选择合适的。
这个有非常多类似案例,比如嵌套 select ,modal, richtext editor 。。。 |