vue2和vue3组件双向绑定

175 阅读2分钟

vue2

单个v-model绑定 model

一个组件上的v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突:

// 父组件
<template>
  <div class="parent">
    <div>父组件中的message:{{ message }}</div>
    -----------------
    <Child v-model="message" />
  </div>
</template>

<script >
  import Child from "@/components/ChildComponent.vue";
  export default {
    components: {
      Child,
    },
    data() {
      return {
        message: "来自父组件的message",
      };
    },
  };
</script>
// 子组件
<template>
  <div>
    <div>接收到的v-model:{{ message }}</div>
    <button @click="changeMessage">子组件改变message</button>
  </div>
</template>

<script>
  export default {
    model: {
      prop: "message",
      event: "changeMessage",
    },
    props: {   //仍然需要再props里面声明
      message: String,
    },
    methods: {
      changeMessage() {
        this.$emit("changeMessage", "子组件改变message");
      },
    },
  };
</script>

注意你仍然需要在组件的 props 选项里声明 message 这个 prop

image.png

image.png

绑定多个prop .sync修饰符

image.png

// 父组件
<template>
  <div class="parent">
    <div>父组件中的message:{{ message }}</div>
    <div>父组件中的title:{{ title }}</div>
    -----------------
    <Child :message.sync="message" :title.sync="title" />
  </div>
</template>

<script >
  import Child from "@/components/ChildComponent.vue";
  export default {
    components: {
      Child,
    },
    data() {
      return {
        message: "来自父组件的message",
        title: "来自父组件的title",
      };
    },
  };
</script>
// 子组件
<template>
  <div>
    <div>子组件接收到message:{{ message }}</div>
    <button @click="changeMessage">子组件改变message</button>
    <div>子组件接收到title:{{ title }}</div>
    <button @click="changeTitle">子组件改变title</button>
  </div>
</template>

<script>
  export default {
    props: {
      message: String,
      title: String,
    },
    methods: {
      changeMessage() {
        this.$emit("update:message", "子组件改变message");
      },
      changeTitle() {
        this.$emit("update:title", "子组件改变title");
      },
    },
  };
</script>

image.png

在$emit中触发的事件要以 "update"+props中的属性名

.sync 合并绑定

修改一下父组件代码,和上面分开绑定一样的效果

<template>
  <div class="parent">
    <div>父组件中的message:{{ info.message }}</div>
    <div>父组件中的title:{{ info.title }}</div>
    -----------------
    <Child v-bind.sync="info" />
  </div>
</template>

<script >
  import Child from "@/components/ChildComponent.vue";
  export default {
    components: {
      Child,
    },
    data() {
      return {
        info: {
          message: "来自父组件的message",
          title: "来自父组件的title",
        },
      };
    },
  };
</script>

vue3

选项式API

一个v-model

// 父组件
<template>
  <div>
    <div>父组件中message:{{ message }}</div>
    ------------- -----------
    <Child v-model="message" />
  </div>
</template>

<script >
  import Child from './components/Child.vue'
  export default {
    components: {
      Child,
    },
    data() {
      return {
        message: '父组件的message',
      }
    },
  }
</script>
// 子组件
<template>
  <div>
    <div>子组件接收到的message:{{ modelValue }}</div>
    <button @click="changeMessage">子组件改变message</button>
  </div>
</template>


<script >
  export default {
    props: {
      modelValue: String,
    },
    emits: ['update:modelValue'],
    methods: {
      changeMessage() {
        this.$emit('update:modelValue', '子组件改变message')
      },
    },
  }
</script>

默认modelValue为prop名,和update:modelValue为事件名

多个v-model

// 父组件
<template>
  <div>父组件中message:{{ message }}</div>
  <div>父组件中title:{{ title }}</div>
  ------------- -----------
  <Child v-model:message="message" v-model:title="title" />
</template>

<script >
  import Child from './components/Child.vue'
  export default {
    components: {
      Child,
    },
    data() {
      return {
        message: '父组件的message',
        title: '父组件的title',
      }
    },
  }
</script>
// 子组件
<template>
  <div>子组件接收到的message:{{ message }}</div>
  <button @click="changeMessage">子组件改变message</button>
  <div>子组件接收到的title:{{ title }}</div>
  <button @click="changeTitle">子组件改变title</button>
</template>


<script >
  export default {
    props: {
      message: String,
      title: String,
    },
    emits: ['update:message', 'update:title'],
    methods: {
      changeMessage() {
        this.$emit('update:message', '子组件改变message')
      },
      changeTitle() {
        this.$emit('update:title', '子组件改变title')
      },
    },
  }
</script>

用v-model:参数="XXX",中的参数来区分不同的prop和事件触发,当只有一个v-model时,也可以用参数形式

组合式API

可以直接在<script setup>中使用编译器宏defineProps和defineEmits,不需要引用

// 父组件
<template>
  <div>父组件中message:{{ message }}</div>
  <div>父组件中title:{{ title }}</div>
  ------------- -----------
  <Child v-model:message="message" v-model:title="title" />
</template>

<script setup>
  import Child from './components/Child.vue'
  import { ref } from 'vue'

  const message = ref('父组件的message')
  const title = ref('父组件的title')
</script>
// 子组件
<template>
  <div>子组件接收到的message:{{ message }}</div>
  <button @click="changeMessage">子组件改变message</button>
  <div>子组件接收到的title:{{ title }}</div>
  <button @click="changeTitle">子组件改变title</button>
</template>


<script setup >
  defineProps({
    message: String,
    title: String,
  })

  const emit = defineEmits(['update:message', 'update:title'])

  function changeMessage() {
    emit('update:message', '子组件改变message')
  }
  function changeTitle() {
    emit('update:title', '子组件改变title')
  }
</script>