【Vue 3通信方式】

vue3父子组件通信方式


前言

父子通信通信的好处:
1.解耦:让组件之间的逻辑和数据传递解耦,降低组件之间的依赖关系,使得组件的设计和开发更加独立和可维护
2.数据管理:可以实现数据的向上传递和向下传递,从而更好地管理数据流。
3.代码的可重复性:父子组件通信之间的数据传递具有可预测性和一致性,这使代码更易于重用。
例如:一个子组件可以将状态传递给多个组件,或者多个子组件可以共享相关的数据源。
4.组件的组合:父子组件通信使得组件的组合变得更加灵活。通过将组件组合树状结构,并使用父子组件通信来传递数据和事件,可以构建出复杂的用户界面。
5.双向数据绑定

总而言之:父子组件通信是Vue 3中实现组件交互、数据传递和组合的重要手段,它有助于提高代码的可维护性、可重性和可扩展性


一、props

1.props是什么?

可以实现父子组件、子父组件、甚至兄弟组件通信,但是:子组件只能只读数据

2.简单案例

父组件,代码如下

	<template>
	  <div class="box">
	    <h1>props:我是父组件曹操</h1>
	    <hr>
	    <Child info="我是曹操" :money="money"></Child>
	  </div>
	</template>
	
	<script setup lang="ts">
	//props:可以实现父子组件通信,props数据还是只读的!!!
	import Child from './Child.vue';
	import {ref} from 'vue';
	let money = ref(10000);
	</script>
	
	<style scoped>
	.box {
	  width: 100vw;
	  height: 400px;
	  background: yellowgreen;
	}
	</style>

子组件,代码如下

	<template>
	  <div class="son">
	       <h1>我是子组件:曹植</h1>
	       <p>{{props.info}}</p>
	       <p>{{props.money}}</p>
	       <!--props可以省略前面的名字-->
	       <p>{{ info }}</p>
	       <p>{{ money }}</p>
	       <button @click="updateProps">修改props的数据</button>
	  </div>
	</template>
	
	<script setup lang="ts">
	//需要使用到defineProps方法去接受父组件传递过来的数据
	//defineProps是Vue3提供方法,不需要引入直接使用
	//数组|对象写法都可以
	let props = defineProps(['info','money']);
	console.log(props);
	//按钮点击的回调
	const updateProps = ()=>{
	  //props.money +=10; props:只读的
	};
	</script>
	
	<style scoped>
	.son{
	  width: 400px;
	  height: 200px;
	  background: hotpink;
	}
	</style>

案例图

在这里插入图片描述

二、自定义事件

1.是什么

在vue框架中事件分为两种:一种是原生的DOM事件,另外一种自定义事件。
原生DOM事件可以让用户于网页进行交互,比如click、dbclick、change、mouseeter…
自定义事件可以实现子组件给父组件传递数据

2.案例

子组件1,代码如下

	<template>
	  <div class="son">
	      <p>我是子组件1</p>
	      <button>点击我也执行</button>
	  </div>
	</template>
	
	<script setup lang="ts">
	
	</script>
	
	<style scoped>
	.son{
	  width: 400px;
	  height: 200px;
	  background: skyblue;
	}
	</style>

子组件2,代码如下

	<template>
	  <div class="child">
	    <p>我是子组件2</p>
	    <button @click="handler">点击我触发自定义事件xxx</button>
	    <button @click="$emit('click','AK47','J220')">点击我出发自定义事件</button>
	  </div>
	</template>
	
	<script setup lang="ts">
	//利用defineEmits方法返回函数触发自定义事件
	//defineEmits方法不需要引入直接使用
	let $emit = defineEmits(['xxx','click']);
	//按钮点击回调
	const handler = ()=>{
	  //第一个参数:事件类型,第二个 | 第N个 N为参数即为注入数据
	  $emit('xxx','东风导弹','航母');
	}
	</script>
	
	<style scoped>
	.child {
	  width: 400px;
	  height: 200px;
	  background: pink;
	}
	</style>

父组件,代码如下

	<template>
	  <div>
	    <h1>事件</h1>
	    <!-- 原生DOM事件 -->
	    <pre @click="handler">
	      大江东去浪淘尽,千古分流人物
	    </pre>
	    <button @click="handler1(1,2,3,$event)">点击我传递多个参数</button>
	    <hr>
	    <!--
	        vue2框架当中:这种写法自定义事件,可以通过.native修饰符变为原生DOM事件
	        vue3框架下面写法其实即为原生DOM事件
	        vue3:原生的DOM事件不管是放在标签身上、组件标签身上都是原生DOM事件
	      -->
	      <Event1 @click="handler2"></Event1>
	    <hr>
	    <!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
	    <Event2 @xxx="handler3" @click="handler4"></Event2>
	  </div>
	</template>
	
	<script setup lang="ts">
	//引入子组件
	import Event1 from './Event1.vue'
	//引入子组件
	import Event2 from './Event2.vue'
	//事件回调--1
	const handler = (event)=>{
	  //event 即为事件对象
	  console.log(event);
	}
	//事件回调--2
	const handler1 = (a,b,c,$event)=>{
	  console.log(a,b,c,$event);
	}
	//事件回调---3
	const handler2 = ()=>{
	  console.log(123)
	}
	//事件回调---4
	const handler3 =(param1, param2)=>{
	  console.log(param1, param2);
	}
	//事件回调--5
	const handler4 = (param1, param2)=>{
	  console.log(param1,param2)
	}
	</script>
	
	<style scoped>
	</style>

案例图

在这里插入图片描述

三、全局事件总线$bus

1.是什么

全局事件总线可以实现任意组件通信,Vue3中使用全局事件总线功能可以使用插件mitt实现

2.案例

依赖

在这里插入图片描述

子组件1,代码如下

	<template>
	  <div class="child1">
	    <h3>我是子组件1:曹植</h3>
	  </div>
	</template>
	
	<script setup lang="ts">
	import $bus from "../../bus";
	//组合式API函数
	import { onMounted } from "vue";
	//组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
	onMounted(()=>{
	  //一个参数:即为事件类型,第二个参数:即为事件回调
	  $bus.on('car',(car)=>{
	    console.log(car)
	  })
	})
	</script>
	
	<style scoped>
	.child1 {
	  width: 300px;
	  height: 300px;
	  background: hotpink;
	}
	</style>

子组件2,代码如下

	<template>
	  <div class="child2">
	     <h2>我是子组件2:曹丕</h2>
	     <button @click="handler">点击我给兄弟送一台法拉利</button>
	  </div>
	</template>
	
	<script setup lang="ts">
	//引入$bus对象
	import $bus from '../../bus';
	//点击按钮回调
	const handler = ()=>{
	  $bus.emit('car',{car:"法拉利"})
	}
	</script>
	
	<style scoped>
	.child2{
	  width: 300px;
	  height: 300px;
	  background: skyblue;
	}
	</style>

父组件,代码如下

	<template>
	  <div class="box">
	    <h1>全局事件总线$bus</h1>
	    <hr />
	    <div class="container">
	      <Child1></Child1>
	      <Child2></Child2>
	    </div>
	  </div>
	</template>
	
	<script setup lang="ts">
	//引入子组件
	import Child1 from "./Child1.vue";
	import Child2 from "./Child2.vue";
	</script>
	
	<style scoped>
	.box {
	  width: 100vw;
	  height: 400px;
	  background: yellowgreen;
	}
	.container{
	  display: flex;
	  justify-content: space-between;
	}
	</style>

案例图

在这里插入图片描述

四、v-model

1.是什么

v-model 指令是收集表单数据(数据双向绑定),除此之外它也可以实现父子组件数据同步
而v-model实际利用props[modelValue]于自定义事件[update:modelValue]实现的。

2.案例

子组件1,代码如下

	<template>
	  <div class="child">
	    <h3>钱数:{{ modelValue }}</h3>
	    <button @click="handler">父子组件数据同步</button>
	  </div>
	</template>
	
	<script setup lang="ts">
	//接受props
	let props = defineProps(['modelValue']);
	let $emit = defineEmits('update:modelValue')
	//子组件内部按钮的点击回调
	const handler = ()=>{
	  $emit('update:modelValue', props.modelValue + 1000);
	}
	</script>
	
	<style scoped>
	.child {
	  width: 600px;
	  height: 300px;
	  background: skyblue;
	}
	</style>

子组件2,代码如下

	<template>
	  <div class="child2">
	    <h1>同时绑定多个v-model</h1>
	    <button @click="handler">pageNo{{ pageNo }}</button>
	    <button @click="$emit('update:pageSize', pageSize + 4)">
	      pageSize{{ pageSize }}
	    </button>
	  </div>
	</template>
	
	<script setup lang="ts">
	let props = defineProps(["pageNo", "pageSize"]);
	let $emit = defineEmits(["update:pageNo", "update:pageSize"]);
	//第一个按钮的事件回调
	const handler = () => {
	  $emit("update:pageNo", props.pageNo + 3);
	};
	</script>
	
	<style scoped>
	.child2 {
	  width: 300px;
	  height: 300px;
	  background: hotpink;
	}
	</style>

父组件,代码如下

	<template>
	  <div>
	    <h1>v-model:钱数{{ money }}{{pageNo}}{{pageSize}}</h1>
	    <input type="text" v-model="info" />
	    <hr />
	    <!-- props:父亲给儿子数据 -->
	    <!-- <Child :modelValue="money" @update:modelValue="handler"></Child> -->
	    <!-- 
	       v-model组件身上使用
	       第一:相当有给子组件传递props[modelValue] = 10000
	       第二:相当于给子组件绑定自定义事件update:modelValue-->
	    <Child v-model="money"></Child>
	    <hr />
	    <Child1 v-model:pageNo="pageNo" v-model:pageSize="pageSize"></Child1>
	  </div>
	</template>
	
	<script setup lang="ts">
	//v-model指令:收集表单数据,数据双向绑定
	//v-model也可以实现组件之间的通信,实现父子组件数据同步的业务
	//父亲给子组件数据 props
	//子组件给父组件数据 自定义事件
	//引入子组件
	import Child from "./Child.vue";
	import Child1 from "./Child1.vue";
	import { ref } from "vue";
	let info = ref("");
	//父组件的数据钱数
	let money = ref(10000);
	//自定义事件的回调
	const handler = (num) => {
	  //将来接受子组件传递过来的数据
	  money.value = num;
	};
	
	//父亲的数据
	let pageNo = ref(1);
	let pageSize = ref(3);
	</script>
	
	<style scoped>
	</style>

案例图
在这里插入图片描述

五、useAttrs

1.是什么

通过useAttrs,我们可以在父组件中获取子组件的属性与事件

2.案例

子组件,代码如下

	<template>
	  <div :title="title">
	     <el-button :="$attrs"></el-button>   
	  </div>
	</template>
	
	<script setup lang="ts">
	//引入useAttrs方法:获取组件标签身上属性与事件
	import {useAttrs} from 'vue';
	//此方法执行会返回一个对象
	let $attrs = useAttrs();
	
	//万一用props接受title
	let props =defineProps(['title']);
	//props与useAttrs方法都可以获取父组件传递过来的属性与属性值
	//但是props接受了useAttrs方法就获取不到了
	console.log($attrs);
	</script>
	
	<style scoped>
	</style>

父组件,代码如下

	<template>
	  <div>
	    <h1>useAttrs</h1>
	    <el-button type="primary" size="small" :icon="Edit"></el-button>
	    <!-- 自定义组件 -->
	    <HintButton type="primary" size="small" :icon="Edit" title="编辑按钮"
	     @click="handler" @xxx="handler"></HintButton>
	  </div>
	</template>
	
	<script setup lang="ts">
	//vue3框架提供一个方法useAttrs方法,它可以获取组件身上的属性与事件!!!
	//图标组件
	import {
	  Check,
	  Delete,
	  Edit,
	  Message,
	  Search,
	  Star,
	} from "@element-plus/icons-vue";
	import HintButton from "./HintButton.vue";
	//按钮点击的回调
	const handler = ()=>{
	  alert(12306);
	}
	</script>
	
	<style scoped>
	</style>

六、ref 与 $parent

1.是什么

ref是一个用于获取组件实例的引用,可以通过在组件上使用ref属性来定义,可以在父组件中获取到子组件的实例,从而访问子组件的属性和方法。

2.案例

子组件ref,代码如下

	<template>
	  <div class="son">
	    <h3>我是子组件:曹植{{money}}</h3>
	  </div>
	</template>
	
	<script setup lang="ts">
	import {ref} from 'vue';
	//儿子钱数
	let money = ref(666);
	const fly = ()=>{
	  console.log('我可以飞');
	}
	//组件内部数据对外关闭的,别人不能访问
	//如果想让外部访问需要通过defineExpose方法对外暴露
	defineExpose({
	  money,
	  fly
	})
	</script>
	
	<style scoped>
	.son {
	  width: 300px;
	  height: 200px;
	  background: cyan;
	}
	</style>

$parent 子组件,代码如下

	<template>
	  <div class="dau">
	     <h1>我是闺女曹杰{{money}}</h1>
	     <button @click="handler($parent)">点击我爸爸给我10000元</button>
	  </div>
	</template>
	
	<script setup lang="ts">
	import {ref} from 'vue';
	//闺女钱数
	let money = ref(999999);
	//闺女按钮点击回调
	const handler = ($parent)=>{
	   money.value+=10000;
	   console.log($parent);
	   $parent.money-=10000;
	}
	</script>
	
	<style scoped>
	.dau{
	  width: 300px;
	  height: 200px;
	  background: hotpink;
	}
	</style>

父组件,代码如下

	<template>
	  <div class="box">
	    <h1>我是父亲曹操:{{money}}</h1>
	    <button @click="handler">找我的儿子曹植借10元</button>
	    <hr>
	    <Son ref="son"></Son>
	    <hr>
	    <Dau></Dau>
	  </div>
	</template>
	
	<script setup lang="ts">
	//ref:可以获取真实的DOM节点,可以获取到子组件实例VC
	//$parent:可以在子组件内部获取到父组件的实例
	//引入子组件
	import Son from './Son.vue'
	import Dau from './Daughter.vue'
	import {ref} from 'vue';
	//父组件的钱数
	let money = ref(100000);
	//获取子组件的实例
	let son = ref();
	//父组件内部按钮点击的回调
	const handler = ()=>{
	  money.value += 10;
	  //儿子钱数减去10
	  son.value.money -= 10;
	  son.value.fly();
	}
	//对外暴露
	defineExpose({
	  money
	})
	</script>
	
	<style scoped>
	.box{
	  width: 100vw;
	  height: 500px;
	  background: skyblue;
	}
	</style>

案例图
在这里插入图片描述

七、provide 与 inject

1.是什么

provide-提供,inject-注入
vue3提供两个方法,provide和inject,可以实现隔辈组件传递参数

2.案例

孙子–子组件,代码如下

	<template>
	  <div class="child1">
	    <h1>孙子组件</h1>
	    <p>{{car}}</p>
	    <button @click="updateCar">更新数据</button>
	  </div>
	</template>
	
	<script setup lang="ts">
	import {inject} from 'vue';
	//注入祖先组件提供数据
	//需要参数:即为祖先提供数据的key
	let car = inject('TOKEN');
	const updateCar = ()=>{
	   car.value  = '自行车';
	}
	</script>
	
	<style scoped>
	.child1 {
	  width: 200px;
	  height: 200px;
	  background: red;
	}
	</style>

儿子-子组件,代码如下

	<template>
	  <div class="child">
	     <h1>我是子组件1</h1>
	     <Child></Child>
	  </div>
	</template>
	
	<script setup lang="ts">
	import Child from './GrandChild.vue';
	</script>
	
	<style scoped>
	.child{
	  width: 300px;
	  height: 400px;
	  background: yellowgreen;
	}
	</style>

父-组件,代码如下

	<template>
	  <div class="box">
	    <h1>Provide与Inject{{car}}</h1>
	    <hr />
	    <Child></Child>
	  </div>
	</template>
	
	<script setup lang="ts">
	import Child from "./Child.vue";
	//vue3提供provide(提供)与inject(注入),可以实现隔辈组件传递数据
	import { ref, provide } from "vue";
	let car = ref("法拉利");
	//祖先组件给后代组件提供数据
	//两个参数:第一个参数就是提供的数据key
	//第二个参数:祖先组件提供数据
	provide("TOKEN", car);
	</script>
	
	<style scoped>
	.box {
	  width: 100vw;
	  height: 600px;
	  background: skyblue;
	}
	</style>

案例图
在这里插入图片描述

八、pinia

1.是什么

pinia官网:https://pinia.web3doc.top/
pinia是集中式状态容器,它允许跨组件或页面共享转态,类似于Vuex,但是核心概念没有mutation、modules

2.案例

在这里插入图片描述

创建小仓库

	//定义info小仓库
	import { defineStore } from "pinia";
	//第一个参数:小仓库名字,第二个参数:小仓库配置对象
	//defineStore方法执行会返回一个函数,函数作用就是让组件可以获取到仓库数据
	let useInfoStore = defineStore("info", {
	    //存储数据:state
	    state: () => {
	        return {
	            count: 99
	        }
	    },
	    actions: {
	        //注意:函数没有context上下文对象
	        updateNum(a: number, b: number) {
	            this.count += a;
	        }
	    },
	    getters: {
	    }
	});

//对外暴露方法
export default useInfoStore;

子组件,代码如下

	<template>
	  <div class="child">
	    <h1>{{ infoStore.count }}</h1>
	    <button @click="updateCount">点击我修改仓库数据</button>
	  </div>
	</template>
	
	<script setup lang="ts">
	import useInfoStore from '../../store/modules/info';
	//获取小仓库对象
	let infoStore = useInfoStore();
	//修改数据方法
	const updateCount = () => {
	  //仓库调用自身的方法去修改仓库的数据
	  infoStore.updateNum(66, 77);
	};
	
	</script>
	
	<style scoped>
	.child {
	  width: 200px;
	  height: 200px;
	  background: yellowgreen;
	}
	</style>

父组件,代码如下

	<template>
	  <div class="box">
	    <h1>pinia</h1>
	    <div class="container">
	      <Child></Child>
	      <Child1></Child1>
	    </div>
	  </div>
	</template>
	
	<script setup lang="ts">
	import Child from "./Child.vue";
	import Child1 from "./Child1.vue";
	//vuex:集中式管理状态容器,可以实现任意组件之间通信!!!
	//核心概念:state、mutations、actions、getters、modules
	
	//pinia:集中式管理状态容器,可以实现任意组件之间通信
	//核心概念:state、actions、getters
	//pinia写法:选择器API、组合式API
	</script>
	
	<style scoped>
	.box {
	  width: 600px;
	  height: 400px;
	  background: skyblue;
	}
	.container{
	  display: flex;
	}
	</style>

9.插槽(slot)

1.是什么

插槽(slot)是组件中用于插入内容或子组件的占位符。它们允许开发者在封装组件时,将不确定的、希望由用户指定的部分定义为插槽。在子组件中,可以使用元素定义插槽,从而用户预留内容占位符。父组件可以在这个占位符中填充任务任何模板代码,如HTML、组件等,填充的内容会替换子组件的标签。
此外,如果子组件没有提供插槽内容,父组件填充的内容将不会生效;相反,如果父组件没有提供填充内容,那么插槽中的备用内容将会启用。
插槽类型:默认插槽、具名插槽、作用域插槽可以实现父子组件通信.


总结

以上就是Vue3 父子组件的通信方式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值