Vue 3 通过双向绑定将反应对象传递给组件

Posted

技术标签:

【中文标题】Vue 3 通过双向绑定将反应对象传递给组件【英文标题】:Vue 3 pass reactive object to component with two way binding 【发布时间】:2021-02-22 19:16:15 【问题描述】:

我在 vue 3 中使用组合 API 对反应性组件进行双向绑定时遇到问题。

设置:

父调用代码为:

<template>
  <h1> message.test </h1>
  <Message v-model="message" />
</template>

<script>
import Message from '@/components/Message.vue';
import  reactive  from 'vue';

export default 
  name: 'Home',
  components:  Message ,

  setup() 
    const message = reactive( test: '123' );

    return 
      message
    ;
  
;
</script>

子组件代码为:

<template>
  <label>
    <input v-model="message" type="text" />
  </label>
</template>

<script>
import  computed  from 'vue';

export default 
  props: 
    messageObj: 
      type: Object,
      default: () => ,
    ,
  ,

  emits: ['update:messageObj'],

  setup(props,  emit ) 
    const message = computed(
      get: () => props.messageObj.test,
      set: (value) => emit('update:messageObj', value),
    );

    return 
      message,
    ;
  ,
;
</script>

问题:

加载组件时,对象的默认值会显示在输入字段中。 这是应该的,但是,当我更新输入框中的值时,父视图中的 H1 不会更新为新的输入框值。

我已经搜索了 *** 板和谷歌,但没有找到任何关于需要做什么来使对象反应的提示。

我阅读了reactivity documentation,但仍然没有找到任何解决我的问题的方法。

为了测试,我已将 message 更改为 ref 并使用这个单一的 ref 值,数据保持反应性,一切都按预期工作。

关于反应性对象不更新可能是什么问题的任何指针?

【问题讨论】:

目前还不清楚您传递给孩子的内容。 v-model:test="message" 在其余代码的上下文中没有意义。您是将整个 reactive 还是仅将一个变量 (test) 传递给孩子? 嗨,Daniel, v-model:test="message" 确实不对,它仍然是我忘记在另一次测试后改回的代码。它应该是js &lt;template&gt; &lt;h1&gt; message.test &lt;/h1&gt; &lt;Message v-model="message" /&gt; &lt;/template&gt; 【参考方案1】:

Here

<div id="app">
    <h1> message.test </h1>
    <child v-model="message"></child>
</div>
const  createApp, reactive, computed  = Vue;


// -------------------------------------------------------------- child
const child = 
    template: `<input v-model="message.test" type="text" />`,
    
    props: 
        modelValue: 
            type: Object,
            default: () => (),
        ,
    ,

    emits: ['update:modelValue'],

    setup(props,  emit ) 
        const message = computed(
            get: () => props.modelValue,
            set: (val) => emit('update:modelValue', val),
        );

        return  message ;
    
;


// ------------------------------------------------------------- parent
createApp(
    components:  child ,

    setup() 
        const message = reactive( test: 'Karamazov' );

        return  message ;
    
).mount('#app');

【讨论】:

嗨,马特,这提供了一个解决方案,但它会将我希望在调用 vue 中拥有的一些逻辑转移到组件中。我的设置的目的是,我最终会在调用 vue 中获得逻辑,该逻辑将传递到添加到该页面的组件上,因此实际上使“哑”组件仅接收来自父级的数据并发出返回的更新以组件的形式。经过进一步的测试,我找到了解决方案。我将在对问题的回答中发表我的观察结果。【参考方案2】:

解决方案和观察:

在调用组件的父视图中,如果您只需要传递对象中的一个值,您可以使用 v-model 并向该 v-model 添加参数。

<template>
  <h1> message.test </h1>
  <!-- <h1> message </h1> -->
  <Message v-model:test="message" />
</template>

<script>
import Message from '@/components/Message.vue';
import  reactive  from 'vue';

export default 
  name: 'Home',
  components:  Message ,

  setup() 
    const message = reactive( test: '123' );

    return 
      message
    ;
  
;
</script>

然后,在接收组件中,您将在 props 中传递的对象的参数注册为对象。

<template>
  <label>
    <input v-model="message.test" type="text" />
  </label>
</template>

<script>
import  computed  from 'vue';

export default 
  props: 
    test: 
      type: Object,
      default: () => 
    ,
  ,

  emits: ['update:test'],

  setup(props,  emit ) 
    const message = computed(
      get: () => props.test,
      set: (value) => emit('update:test', value),
    );

    return 
      message,
    ;
  ,
;
</script>

如果您需要传递整个对象,您需要将其用作组件中的道具,名称为 modelValue。

与之前的代码相比,父代的变化:

<template>
  <h1> message.test </h1>
  <!-- <h1> message </h1> -->
  <Message v-model="message" />
</template>

组件代码:

<template>
  <label>
    <input v-model="message.test" type="text" />
  </label>
</template>

<script>
import  computed  from 'vue';

export default 
  props: 
    modelValue: 
      type: Object,
      default: () => 
    ,
  ,

  emits: ['update:modelValue'],

  setup(props,  emit ) 
    const message = computed(
      get: () => props.modelValue,
      set: (value) => emit('update:modelValue', value),
    );

    return 
      message,
    ;
  ,
;
</script>

【讨论】:

太棒了!示例非常干净,传入整个对象正是我想要的:谢谢: 向你致敬!它为我节省了很多时间。【参考方案3】:

应该很简单,不需要计算。请参见下面的示例。

messageObj 在子组件中被替换为 message 以使发射工作(由于此演示中区分大小写,这将中断)

const app = Vue.createApp(
  setup() 
    const message = Vue.reactive( test: '123' , foo: "bark");

    return 
      message,
    ;
  
)

app.component('Message', 
  props: 
    message: 
      type: Object,
      default: () => ,
    ,
  ,
  emits: ['update:message'],
  setup(props,  emit ) 
    const message = props.message;
    return  message ;
  ,
  
  template: document.querySelector('#t_child')
)

app.mount('#app')
<script src="https://unpkg.com/vue@3.0.2/dist/vue.global.prod.js"></script>

<fieldset>
  <div id="app">
    <h1> message.test  ||  message.foo </h1>
    <fieldset><Message v-model:message="message"/></fieldset>
  </div>
</fieldset>

<template id="t_child">
  <label>
    <h4>message</h4>
    <input v-model="message.test" type="text" />
    <input v-model="message.foo" type="text" />
  </label>
</template>

【讨论】:

camelCase var 也咬了我,谢谢你指出这一点。 当我尝试使用此代码时,我收到了来自 ESLint 的消息:“在 setup() 的根范围内从 props 获取值将导致该值失去反应性。”

以上是关于Vue 3 通过双向绑定将反应对象传递给组件的主要内容,如果未能解决你的问题,请参考以下文章

vue组件双向绑定key的作用

Vue中父子组件的双向数据绑定

Vue中父子组件的双向数据绑定

Vue中父子组件的双向数据绑定

vue 自定义组件 v-model双向绑定 父子组件同步通信

vue父子组件数据传输以及实现父子组件数据双向绑定