【问题标题】:Vue 3 pass reactive object to component with two way bindingVue 3 通过双向绑定将反应对象传递给组件
【发布时间】: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 不会更新为新的输入框值。

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

我阅读了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;

标签: vue.js vue-component vuejs3


【解决方案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 获取值将导致该值失去反应性。”
猜你喜欢
  • 2019-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多