【Vue3】组件通信之v-model

【Vue3】组件通信之v-model

背景

随着年龄的增长,很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来,技术出身的人总是很难放下一些执念,遂将这些知识整理成文,以纪念曾经努力学习奋斗的日子。本文内容并非完全原创,大多是参考其他文章资料整理所得,感谢每位技术人的开源精神。

简介

本文介绍 Vue3 中如何使用 v-model 实现组件间通信,即组件间相互传数据。

Vue3 中组件间通信包括:

  • 父组件向子组件传数据,实现方案有:
    • props
    • v-model
    • $refs
    • 默认插槽 / 具名插槽
  • 子组件向父组件传数据
    • props
    • v-model
    • $parent
    • 自定义事件
    • 作用域插槽
  • 父组件与子组件的子组件互传数据,即与孙子组件互传数据
    • $attrs
    • provider & inject
  • 任意组件间传数据
    • mitt
    • Pinia

开发环境

分类名称版本
操作系统WindowsWindows 11
IDEVisual Studio Code1.91.1

开发步骤及源码

1> 创建 Vue3 工程,参考:【Vue3】工程创建及目录说明

2> 删除 src 目录下 assetscomponents 目录。

3> 修改 src 目录下 main.ts

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

4> 定义子组件,接收来自父组件的数据。

<template>
    <div class="content">
        <h1>子组件</h1>
        <span>用户名:</span>
        <input type="text" 
            :value="modelValue"
            @input="emits('update:modelValue', (<HTMLInputElement>$event.target).value)" />
    </div>
</template>

<script setup lang="ts">
defineProps(['modelValue'])
const emits = defineEmits(['update:modelValue'])
</script>

<style scoped lang="scss">
.content {
    background-color: greenyellow;
    padding: 20px;
    input {
        border: 3px solid red;
        height: 30px;
        line-height: 30px;
        width: 300px;
    }
}
</style>

父组件通过 v-model 向子组件传数据,Vue3 框架默认 v-model 传的数据名为 modelValue,对应事件名为 update:modelValue,所以子组件需要使用 defineProps 函数声明接收来自父组件的数据 modelValue,使用 defineEmits 函数声明接收来自父组件的事件 update:modelValue
触发事件函数的参数是 $event.target.value,即子组件中 DOM(input)事件对象的值。
注意:需要执行 npm install -D sass 命令安装 CSS 预处理器。

5> 修改 Vue 根组件 src/App.vue,使用 v-model 向子组件传数据。

<template>
  <div class="parent">
    <Login v-model="username" />
  </div>
</template>

<script setup lang="ts">
import Login from './components/Login.vue'
import { ref, watch } from 'vue'

const username = ref('administrator')
watch(username, (newValue, oldValue) => {
  console.log('username changed from', oldValue, 'to', newValue)
})
</script>

<style scoped lang="scss">
.parent {
  background-color: orange;
  padding: 20px;
}
</style>

以上代码中 <Login v-model="username" /> 等同于 <Login :modelValue="username" @update:modelValue="username = $event" />,后者为 v-model 的本质。

6> 执行命令 npm run dev 启动应用,浏览器访问:http://localhost:5173/
在这里插入图片描述
页面初始化时 <input> 框内显示来自父组件的数据 administrator,每次修改 <input> 框中数据,控制台便会打印出数据变化日志,此日志为 App.vue 打印的,表明父组件也收到了子组件传来的修改后的变更数据。

7> Vue3 默认 v-model 数据名是 modelValue,此名称可以自定义。自定义 v-model 数据名便于在同一组件标签上使用多个 v-model 属性传数据,修改 App.vue 向子组件传两个数据。

<template>
  <div class="parent">
    <Login v-model:account="username" v-model:credential="password" />
  </div>
</template>

<script setup lang="ts">
import Login from './components/Login.vue'
import { ref, watch } from 'vue'

const username = ref('administrator')
const password = ref('00000000')
watch([username, password], (newValue, oldValue) => {
  console.log('username changed from', oldValue, 'to', newValue)
})
</script>

<style scoped lang="scss">
.parent {
  background-color: orange;
  padding: 20px;
}
</style>

8> 修改子组件,声明接收父组件的两个数据并进行处理。

<template>
    <div class="content">
        <h1>子组件</h1>
        <span>用户名:</span>
        <input type="text" 
            :value="account"
            @input="emits('update:account', (<HTMLInputElement>$event.target).value)" />
        <span>密码:</span>
        <input type="text" 
            :value="credential"
            @input="emits('update:credential', (<HTMLInputElement>$event.target).value)" />
    </div>
</template>

<script setup lang="ts">
defineProps(['account', 'credential'])
const emits = defineEmits(['update:account', 'update:credential'])
</script>

<style scoped lang="scss">
.content {
    background-color: greenyellow;
    padding: 20px;
    input {
        border: 3px solid red;
        height: 30px;
        line-height: 30px;
        margin-right: 20px;
        width: 300px;
    }
}
</style>

9> 浏览器刷新访问:http://localhost:5173/,页面初始化时 <input> 框内显示来自父组件的数据 administrator00000000,每次修改 <input> 框中数据,控制台便会打印出数据变化日志,此日志为 App.vue 打印的,表明父组件也收到了子组件传来的修改后的变更数据。
在这里插入图片描述

总结

  • 使用 v-model 实现组件间通信的方法常用于封装自定义 UI 组件库,在日常业务开发过程中较少使用;
  • 使用 v-model 实现组件间通信的底层原理是:动态 value + input 事件
  • 父组件需要在子组件标签上通过 v-model 属性标识所传的数据;
  • 子组件需要使用 defineProps 函数声明接收父组件的数据,使用 defineEmits 函数声明接收父组件数据对应的事件;
  • v-model 默认传的数据名为 modelValue,对应事件名为 update:modelValue。数据名可自定义,格式:v-model:自定义数据名;事件名前缀固定为 update:,格式:update:自定义数据名
  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Vue 2 中,父子组件之间可以通过 `v-model` 实现双向通信。下面是一个示例: 父组件: ```vue <template> <div> <p>父组件的值:{{ parentValue }}</p> <ChildComponent v-model="parentValue"></ChildComponent> </div> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent }, data() { return { parentValue: '' }; } }; </script> ``` 子组件(ChildComponent.vue): ```vue <template> <div> <p>子组件的值:{{ childValue }}</p> <input v-model="childValue" /> </div> </template> <script> export default { props: ['value'], data() { return { childValue: this.value }; }, watch: { value(newVal) { this.childValue = newVal; }, childValue(newVal) { this.$emit('input', newVal); } } }; </script> ``` 在父组件中,使用 `v-model` 绑定到一个名为 `parentValue` 的 data 属性上,然后将该属性传递给子组件。子组件中,通过 `props` 接收父组件传递的值,并在子组件内部定义一个名为 `childValue` 的 data 属性来保存子组件的值。同时,使用 `watch` 监听父组件传递的值变化,并将新值赋给 `childValue`。当子组件的值发生变化时,通过 `$emit` 方法触发名为 `input` 的自定义事件,将新值传递给父组件。 这样,父子组件之间就可以通过 `v-model` 实现双向通信了。当父组件的值发生改变时,子组件会随之更新;当子组件的值发生改变时,也会同步更新到父组件中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

又言又语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值