Vue3中的常见组件通信之props和自定义事件

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 准备父子组件

首先准备一个简单的父子组件的样式,如下是父组件代码:

vue 复制代码
<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>

以下是子组件代码:

vue 复制代码
<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实现父传子通信,可以用标签中的属性值直接传递数据,父组件代码中只需要在子组件标签上添加属性值即可,如下代码,传递两个数据:

vue 复制代码
<Child :f2CMsg="f2CMsg" :f2CObj="f2CObj"/>

定义传递的数据内容,两个数据分别是ref定义的基本类型的响应式数据和reactive定义的对象类型的响应式数据。

typescript 复制代码
//引入ref和reactive
import {ref,reactive} from 'vue'

//数据,ref定义的基本类型的响应式数据
let f2CMsg =ref('你好,子组件。')

//数据,reactive定义的对象类型的响应式数据
let f2CObj = reactive({
id:"asdfg01",
name:"张三",
age:18,
detail:"这是父组件中的对象信息"
})

在子组件中需要声明接收数据props,注意接收的数据格式是数组,哪怕是只传递一个数据也要写成数组的形式。

typescript 复制代码
//声明接收数据props
defineProps(['f2CMsg','f2CObj'])

子组件中在页面展示接收的数据,如下代码:

vue 复制代码
<h4>父组件传递的信息:{{ f2CMsg }}</h4>
<h4>父组件传递的对象:</h4>
<ul>
    <li>姓名:{{f2CObj.name}}</li>
    <li>年龄:{{f2CObj.age}}</li>
    <li>详情:{{f2CObj.detail}}</li>
</ul>

然后运行结果如下:

1.3 子传父通信的实现

用props实现子传父的通信需要父组件先向子组件传递一个函数,然后传递的数据以参数的形式传递给函数,父组件中先定义子传父的数据变量和函数或者方法如下:

typescript 复制代码
//定义子传父的数据变量
let c2FMsg = ref('')
//定义方法传递给子组件
function getMsg(value:string){
    c2FMsg.value = value
}

在标签中把方法传给子组件:

vue 复制代码
<Child :f2CMsg="f2CMsg" :f2CObj="f2CObj" :sendMsg="getMsg"/>

在子组件中接收数据,并定义数据内容:

typescript 复制代码
defineProps(['f2CMsg','f2CObj','sendMsg'])
let c2FMsg = ref('你好,父组件。')

在子组件中设置一个按钮,给按钮绑定点击事件,点击事件触发sendMsg方法,并传递参数:

vue 复制代码
<button @click="sendMsg(c2FMsg)">点我向父组件传递信息</button>

现在数据应该已经传递给了父组件,在父组件可以用如下代码展示:

vue 复制代码
<h4 v-show="c2FMsg">子组件传递的信息:{{ c2FMsg }}</h4>

运行结果如下:

点击按钮后运行结果如下:

至此我们已经用props实现了子传父的通信功能。当然子传父也可以传递对象。此处不再展示。如下是完整代码:

父组件中的代码:

vue 复制代码
<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>

子组件的代码:

vue 复制代码
<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 自定义事件实现子传父通信

首先在子组件中定义要传递的数据,此次依然已字符串为例,如下代码:

typescript 复制代码
let c2FMsg = ref('你好,父组件。')

然后在父组件中定义接收的数据变量,并声明函数,用来保存接收的数据。

typescript 复制代码
let c2FMsg = ref('')

//声明函数saveMsg,用来保存接收的数据
function saveMsg(value:string){
    c2FMsg.value = value
}

之后给子组件绑定自定义事件,事件名为send-message,同时将函数saveMsg传进去,注意自定义事件名的命名规范官方建议采用肉串形式的命名方式。

vue 复制代码
<!-- 给子组件绑定自定义事件 -->
<Child @send-message="saveMsg"/>

在子组件中需要声明事件

typescript 复制代码
//声明事件
let emit = defineEmits(['send-message'])

触发事件的代码如下:

typescript 复制代码
emit('send-message')

只要在子组件中写出上面触发事件的代码,就可以实现子传父的通信,这次以子组件挂载3秒后自动触发事件为例,如下代码:

typescript 复制代码
//组件挂载3秒后触发事件
onMounted(()=>{
	setTimeout(()=>{
		//触发事件send-message,并传c2FMsg
		emit('send-message',c2FMsg)
	},3000)
})

此时已经实现了子传父的通信,最后在父组件中展示出来,如下代码:

typescript 复制代码
<h3 v-show="c2FMsg">子组件传递的信息:{{ c2FMsg }}</h3>

最后运行页面效果如下,在刚启动页面是如下效果:

3秒钟之后看下的效果如下图所示:

以下是完整代码:

父组件代码:

typescript 复制代码
<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>

以下是子组件代码:

typescript 复制代码
<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 小结

以上便是自定义事件的基本用法,在实际开发中是比较常用的用来实现子传父的通信方式。

相关推荐
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
CodeClimb5 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
沈梦研5 小时前
【Vscode】Vscode不能执行vue脚本的原因及解决方法
ide·vue.js·vscode
hunter2062065 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb5 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角5 小时前
CSS 颜色
前端·css
轻口味6 小时前
Vue.js 组件之间的通信模式
vue.js
浪浪山小白兔6 小时前
HTML5 新表单属性详解
前端·html·html5
lee5767 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579657 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter