v-model与scoped slots绑定失效?子组件数据未更新原因咨询
这个问题我之前在项目里也踩过坑,核心在于Vue插槽的作用域传递规则和单向数据流的限制,咱们一步步拆解原因,再给出可行的解决办法:
为什么双向绑定没生效?
1. 插槽作用域的传值是单向只读的
你子组件通过:name="name"传给插槽的,其实是子组件data中name的单向快照,父组件通过slot-scope接收的props.name并不是和子组件name直接绑定的引用。
简单说:父组件拿到的props.name只是子组件给的一个“副本”,你在父组件输入框里修改这个值,只会改变父组件这边临时存储的这个值,不会反向通知子组件去更新它自己data里的name。而且Vue本身就禁止直接修改从父级传递过来的属性(插槽作用域属性也遵循这个规则),避免数据流混乱。
2. v-model的语法糖在这里不适用
v-model本质是value="xxx"加上@input="xxx = $event.target.value"的语法糖,但这里你绑定的props.name是只读的,父组件修改它并不会触发子组件的任何更新逻辑——子组件根本不知道父组件改了这个值,自然也不会同步自己的name。
怎么解决这个问题?
要实现父子组件通过插槽的双向同步,得遵循Vue的单向数据流原则:子组件暴露一个修改自身name的方法,父组件通过调用这个方法来通知子组件更新。
方案:子组件提供更新方法,父组件触发
子组件代码修改:
<template> <slot name="test" :name="name" :updateName="updateName"> <input type="text" v-model="name"> </slot> </template> <script> export default { data() { return { name: '' } }, methods: { updateName(newName) { // 子组件自己更新data里的name this.name = newName } } } </script>
父组件代码修改:
<template> <ChildComponent> <div slot="test" slot-scope="props"> <!-- 手动绑定value,并用子组件的updateName方法触发更新 --> <input type="text" :value="props.name" @input="props.updateName($event.target.value)"> </div> </ChildComponent> </template>
这样一来,父组件输入框的内容变化会调用子组件的updateName方法,子组件自己更新data里的name,同时插槽传递的name也会同步更新,实现双向关联。
如果你的项目用的是Vue 2.6+的v-slot语法(更推荐的新写法),父组件可以改成:
<template> <ChildComponent> <template v-slot:test="props"> <input type="text" :value="props.name" @input="props.updateName($event.target.value)"> </template> </ChildComponent> </template>
不推荐的“取巧”方式(了解即可)
你也可以把name包装成一个对象传给插槽(比如:nameObj="{name: name}"),因为对象是引用类型,父组件修改props.nameObj.name时会直接修改子组件的对象属性,实现同步。但这种方式违反了单向数据流原则,会让组件间的数据流变得模糊,后期维护很麻烦,所以不建议使用。
内容的提问来源于stack exchange,提问作者panthro

