【Vue3】组件通信之v-model
- 背景
- 简介
- 开发环境
- 开发步骤及源码
- 总结
背景
随着年龄的增长,很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来,技术出身的人总是很难放下一些执念,遂将这些知识整理成文,以纪念曾经努力学习奋斗的日子。本文内容并非完全原创,大多是参考其他文章资料整理所得,感谢每位技术人的开源精神。
简介
本文介绍 Vue3 中如何使用 v-model
实现组件间通信,即组件间相互传数据。
Vue3 中组件间通信包括:
- 父组件向子组件传数据,实现方案有:
props
v-model
$ref
- 默认插槽 / 具名插槽
- 子组件向父组件传数据
props
v-model
$parent
- 自定义事件
- 作用域插槽
- 父组件向子组件的子组件传数据,即向孙子组件传数据
$attrs
provider
&inject
- 任意组件间传数据
mitt
Pinia
开发环境
分类 | 名称 | 版本 |
---|---|---|
操作系统 | Windows | Windows 11 |
IDE | Visual Studio Code | 1.91.1 |
开发步骤及源码
1> 创建 Vue3 工程,参考:【Vue3】工程创建及目录说明。
2> 删除 src
目录下 assets
和 components
目录。
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>
框内显示来自父组件的数据 administrator
和 00000000
,每次修改 <input>
框中数据,控制台便会打印出数据变化日志,此日志为 App.vue
打印的,表明父组件也收到了子组件传来的修改后的变更数据。
总结
- 使用
v-model
实现组件间通信的方法常用于封装自定义 UI 组件库,在日常业务开发过程中较少使用; - 使用
v-model
实现组件间通信的底层原理是:动态 value
+input 事件
; - 父组件需要在子组件标签上通过
v-model
属性标识所传的数据; - 子组件需要使用
defineProps
函数声明接收父组件的数据,使用defineEmits
函数声明接收父组件数据对应的事件; v-model
默认传的数据名为modelValue
,对应事件名为update:modelValue
。数据名可自定义,格式:v-model:自定义数据名
;事件名前缀固定为update:
,格式:update:自定义数据名
。