Vue3中的常见组件通信
概述
在vue3中常见的组件通信有props、mitt、v-model、 r e f s 、 refs、 refs、parent、provide、inject、pinia、slot等。不同的组件关系用不同的传递方式。常见的撘配形式如下表所示。
组件关系 | 传递方式 |
---|---|
父传子 | 1. props 2. v-model 3. $refs 4. 默认插槽、具名插槽 |
子传父 | 1. props 2. 自定义事件 3. v-model 4. $parent 5. 作用域插槽 |
祖传孙、孙传祖 | 1. $attrs 2. provide、inject |
兄弟间、任意组件间 | 1. mitt 2. pinia |
本文讲述props和自定义事件,其他的通信方式后面再更新。
1.props
1.1 准备父子组件
首先准备一个简单的父子组件的样式,如下是父组件代码:
<template>
<div class="father">
<h2>这是父组件</h2>
<Child/>
</div>
</template>
<script setup lang="ts" name="Father">
//引入子组件
import Child from './Child.vue';
</script>
<style scoped>
.father{
margin: 5px;
background-color:rgb(205, 165, 32);
padding: 20px;
color: white;
}
</style>
以下是子组件代码:
<template>
<div class="child">
<h2>这是子组件</h2>
</div>
</template>
<script setup lang="ts" name="Child">
</script>
<style scoped>
.child{
margin: 5px;
background-color: rgba(93, 135, 20, 0.224);
border: 1px solid;
border-color: white;
box-shadow: 0 0 5px;
padding: 10px;
color: #000;
}
</style>
运行结果如下所示:
1.2 父传子通信的实现
用props实现父传子通信,可以用标签中的属性值直接传递数据,父组件代码中只需要在子组件标签上添加属性值即可,如下代码,传递两个数据:
<Child :f2CMsg="f2CMsg" :f2CObj="f2CObj"/>
定义传递的数据内容,两个数据分别是ref定义的基本类型的响应式数据和reactive定义的对象类型的响应式数据。
//引入ref和reactive
import {ref,reactive} from 'vue'
//数据,ref定义的基本类型的响应式数据
let f2CMsg =ref('你好,子组件。')
//数据,reactive定义的对象类型的响应式数据
let f2CObj = reactive({
id:"asdfg01",
name:"张三",
age:18,
detail:"这是父组件中的对象信息"
})
在子组件中需要声明接收数据props,注意接收的数据格式是数组,哪怕是只传递一个数据也要写成数组的形式。
//声明接收数据props
defineProps(['f2CMsg','f2CObj'])
子组件中在页面展示接收的数据,如下代码:
<h4>父组件传递的信息:{{ f2CMsg }}</h4>
<h4>父组件传递的对象:</h4>
<ul>
<li>姓名:{{f2CObj.name}}</li>
<li>年龄:{{f2CObj.age}}</li>
<li>详情:{{f2CObj.detail}}</li>
</ul>
然后运行结果如下:
1.3 子传父通信的实现
用props实现子传父的通信需要父组件先向子组件传递一个函数,然后传递的数据以参数的形式传递给函数,父组件中先定义子传父的数据变量和函数或者方法如下:
//定义子传父的数据变量
let c2FMsg = ref('')
//定义方法传递给子组件
function getMsg(value:string){
c2FMsg.value = value
}
在标签中把方法传给子组件:
<Child :f2CMsg="f2CMsg" :f2CObj="f2CObj" :sendMsg="getMsg"/>
在子组件中接收数据,并定义数据内容:
defineProps(['f2CMsg','f2CObj','sendMsg'])
let c2FMsg = ref('你好,父组件。')
在子组件中设置一个按钮,给按钮绑定点击事件,点击事件触发sendMsg方法,并传递参数:
<button @click="sendMsg(c2FMsg)">点我向父组件传递信息</button>
现在数据应该已经传递给了父组件,在父组件可以用如下代码展示:
<h4 v-show="c2FMsg">子组件传递的信息:{{ c2FMsg }}</h4>
运行结果如下:
点击按钮后运行结果如下:
至此我们已经用props实现了子传父的通信功能。当然子传父也可以传递对象。此处不再展示。如下是完整代码:
父组件中的代码:
<template>
<div class="father">
<h2>这是父组件</h2>
<h4 v-show="c2FMsg">子组件传递的信息:{{ c2FMsg }}</h4>
<Child :f2CMsg="f2CMsg" :f2CObj="f2CObj" :sendMsg="getMsg"/>
</div>
</template>
<script setup lang="ts" name="Father">
//引入子组件
import Child from './Child.vue';
//引入ref和reactive
import {ref,reactive} from 'vue'
//数据,ref定义的基本类型的响应式数据
let f2CMsg =ref('你好,子组件。')
//数据,reactive定义的对象类型的响应式数据
let f2CObj = reactive({
id:"asdfg01",
name:"张三",
age:18,
detail:"这是父组件中的对象信息"
})
//定义子传父的数据变量
let c2FMsg = ref('')
//定义方法传递给子组件
function getMsg(value:string){
c2FMsg.value = value
}
</script>
<style scoped>
.father{
margin: 5px;
background-color:rgb(205, 165, 32);
padding: 20px;
color: white;
}
</style>
子组件的代码:
<template>
<div class="child">
<h2>这是子组件</h2>
<h4>父组件传递的信息:{{ f2CMsg }}</h4>
<h4>父组件传递的对象:</h4>
<ul>
<li>姓名:{{f2CObj.name}}</li>
<li>年龄:{{f2CObj.age}}</li>
<li>详情:{{f2CObj.detail}}</li>
</ul>
<button @click="sendMsg(c2FMsg )">点我向父组件传递信息</button>
</div>
</template>
<script setup lang="ts" name="Child">
import { ref,reactive } from 'vue';
//声明接收数据props
defineProps(['f2CMsg','f2CObj','sendMsg'])
let c2FMsg = ref('你好,父组件。')
</script>
<style scoped>
.child{
margin: 5px;
background-color: rgba(93, 135, 20, 0.224);
border: 1px solid;
border-color: white;
box-shadow: 0 0 5px;
padding: 10px;
color: #000;
}
</style>
1.4 小结
用props实现父子通信的步骤是这样的:
父传子:父组件中定义传递的数据–>标签中用属性值直接传递数据–>子组件中声明接收数据–>用插值语法展示数据。
子传父: 父组件先定义接收数据的变量–>父组件中定义函数–>将函数传递给子组件–>子组件中声明接收数据,接收的数据为函数–>子组件中定义传递的数据–>子组件中调用接收的函数,将定义的要传递的数据作为参数进行传递–>父组件中收到数据,可以在页面中展示。
最后总结如下:
父传子:标签中的属性值是非函数
子传父:标签中的属性值是函数
2. 自定义事件
自定义事件通常用于子传父,需要注意在原生事件中事件名是特定的,比如click,keyup等,在自定义事件中事件名是任意的;在原生事件中事件对象 e v e n t 是包含事件相关信息的对象( ‘ p a g e X ‘ 、 ‘ p a g e Y ‘ 、 ‘ t a r g e t ‘ 、 ‘ k e y C o d e ‘ ),在定义事件中事件对象 event是包含事件相关信息的对象(`pageX`、`pageY`、`target`、`keyCode`),在定义事件中事件对象 event是包含事件相关信息的对象(‘pageX‘、‘pageY‘、‘target‘、‘keyCode‘),在定义事件中事件对象event是调用emit所提供的数据,可以是任意类型。
2.1 准备父子组件
父子组件代码与样式与本文中1.1中的完全相同,此处不再赘述。
2.2 自定义事件实现子传父通信
首先在子组件中定义要传递的数据,此次依然已字符串为例,如下代码:
let c2FMsg = ref('你好,父组件。')
然后在父组件中定义接收的数据变量,并声明函数,用来保存接收的数据。
let c2FMsg = ref('')
//声明函数saveMsg,用来保存接收的数据
function saveMsg(value:string){
c2FMsg.value = value
}
之后给子组件绑定自定义事件,事件名为send-message,同时将函数saveMsg传进去,注意自定义事件名的命名规范官方建议采用肉串形式的命名方式。
<!-- 给子组件绑定自定义事件 -->
<Child @send-message="saveMsg"/>
在子组件中需要声明事件
//声明事件
let emit = defineEmits(['send-message'])
触发事件的代码如下:
emit('send-message')
只要在子组件中写出上面触发事件的代码,就可以实现子传父的通信,这次以子组件挂载3秒后自动触发事件为例,如下代码:
//组件挂载3秒后触发事件
onMounted(()=>{
setTimeout(()=>{
//触发事件send-message,并传c2FMsg
emit('send-message',c2FMsg)
},3000)
})
此时已经实现了子传父的通信,最后在父组件中展示出来,如下代码:
<h3 v-show="c2FMsg">子组件传递的信息:{{ c2FMsg }}</h3>
最后运行页面效果如下,在刚启动页面是如下效果:
3秒钟之后看下的效果如下图所示:
以下是完整代码:
父组件代码:
<template>
<div class="father">
<h2>这是父组件</h2>
<h3 v-show="c2FMsg">子组件传递的信息:{{ c2FMsg }}</h3>
<!-- 给子组件绑定自定义事件 -->
<Child @send-message="saveMsg"/>
</div>
</template>
<script setup lang="ts" name="Father">
//引入子组件
import Child from './Child.vue';
import { ref } from 'vue';
let c2FMsg = ref('')
//声明函数,用来保存接收的数据
function saveMsg(value:string){
c2FMsg.value = value
}
</script>
<style scoped>
.father{
margin: 5px;
background-color:rgb(205, 165, 32);
padding: 20px;
color: white;
}
</style>
以下是子组件代码:
<template>
<div class="child">
<h2>这是子组件</h2>
</div>
</template>
<script setup lang="ts" name="Child">
import {ref,onMounted} from 'vue'
let c2FMsg = ref('你好,父组件。')
//声明事件
let emit = defineEmits(['send-message'])
//组件挂载3秒后触发事件
onMounted(()=>{
setTimeout(()=>{
//触发事件send-message,并传c2FMsg
emit('send-message',c2FMsg)
},3000)
})
</script>
<style scoped>
.child{
margin: 5px;
background-color: rgba(93, 135, 20, 0.224);
border: 1px solid;
border-color: white;
box-shadow: 0 0 5px;
padding: 10px;
color: #000;
}
</style>
2.3 小结
以上便是自定义事件的基本用法,在实际开发中是比较常用的用来实现子传父的通信方式。