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 父子组件的通信方式