当前位置: 首页 > news >正文

6、组件通信详解(父子、兄弟、祖孙)

一、父传子

1、props

  • 用法:

(1)父组件用 props绑定数据,表示为 v-bind:props="数据" (v-bind:简写为 : ,props可以任意命名)

(2)子组件用 defineProps(['props',....])接收

  • 注意:

(1)v-bind:c="数据" 表示父组件给数据绑定了一个名为c的prop属性。这样当父组件的数据发生改变,子组件也能接收到,使双方数据同步;

(2)父传子prop是非函数;

(3)props绑定的数据是只读属性,不能进行修改

父组件Father.vue

<template><div class="father"><h3>父组件</h3><h4>汽车:{{ car }}</h4><Child v-bind:c="car"/></div>
</template><script setup lang="ts" name="Father">
// @ts-nocheckimport Child from './Child.vue'import {ref} from 'vue'// 数据let car = ref('奔驰')
</script>

子组件 Child.vue

<template><div class="child"><h3>子组件</h3><h4>父给的车:{{ c }}</h4></div>
</template><script setup lang="ts" name="Child">
// @ts-nocheckimport {ref} from 'vue'// 声明接收propsdefineProps(['c'])
</script>

2、v-model

 用法:

(1)父组件 使用v-model给数据绑定

(2)子组件 利用了props和自定义事件的组合使用

 父组件

<Child1 v-model:pageNo="pageNo" v-model:pageSize="pageSize"></Child1>
//父亲的数据
let pageNo = ref(1)
let pageSize = ref(3)

子组件

<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>

3、$refs

用法:

ref

 (1)父组件 给所有子组件用ref打标识,定义点击事件以及回调。

(2)子组件 defineExpose暴露自己的数据,父亲可以拿到

$refs

父亲想修改所有儿子的数据。

(1)父组件定义一个修改全部儿子的方法传入参数$refs。$refs可以获取所有儿子实例

(2)子组件都使用defineExpose向外暴露自己的数据

父组件:

<template><div class="father"><h3>父组件</h3><h4>房产:{{ house }}</h4><button @click="changeToy">修改Child1的玩具</button><button @click="changeComputer">修改Child2的电脑</button><button @click="getAllChild($refs)">让所有孩子的书变多</button><Child1 ref="c1"/><Child2 ref="c2"/></div>
</template><script setup lang="ts" name="Father">import Child1 from './Child1.vue'import Child2 from './Child2.vue'import { ref,reactive } from "vue";let c1 = ref()let c2 = ref()// 注意点:当访问obj.c的时候,底层会自动读取value属性,因为c是在obj这个响应式对象中的/* let obj = reactive({a:1,b:2,c:ref(3)})let x = ref(4)console.log(obj.a)console.log(obj.b)console.log(obj.c)console.log(x) */// 数据let house = ref(4)// 方法function changeToy(){c1.value.toy = '小猪佩奇'}function changeComputer(){c2.value.computer = '华为'}function getAllChild(refs:{[key:string]:any}){console.log(refs)for (let key in refs){refs[key].book += 3}}// 向外部提供数据defineExpose({house})
</script>

子组件1:

<template><div class="child1"><h3>子组件1</h3><h4>玩具:{{ toy }}</h4><h4>书籍:{{ book }} 本</h4><button @click="minusHouse($parent)">干掉父亲的一套房产</button></div>
</template><script setup lang="ts" name="Child1">import { ref } from "vue";// 数据let toy = ref('奥特曼')let book = ref(3)// 方法function minusHouse(parent:any){parent.house -= 1}// 把数据交给外部defineExpose({toy,book})</script>

 子组件2:

<template><div class="child2"><h3>子组件2</h3><h4>电脑:{{ computer }}</h4><h4>书籍:{{ book }} 本</h4></div>
</template><script setup lang="ts" name="Child2">import { ref } from "vue";// 数据let computer = ref('联想')let book = ref(6)// 把数据交给外部defineExpose({computer,book})
</script>

4、默认插槽

用法:

(1)父组件  使用category子组件的起始终止标签,里面夹带要展示的内容

(2)子组件 使用slot默认插槽,父组件的每一个category标签中间的内容都会插入到slot里面

这样就能把父亲里面的数据显示到子组件里面。

父组件

<template><div class="father"><h3>父组件</h3><div class="content"><Category title="热门游戏列表"><ul><li v-for="g in games" :key="g.id">{{ g.name }}</li></ul></Category><Category title="今日美食城市"><img :src="imgUrl" alt=""></Category><Category title="今日影视推荐"><video :src="videoUrl" controls></video></Category></div></div>
</template><script setup lang="ts" name="Father">import Category from './Category.vue'import { ref,reactive } from "vue";let games = reactive([{id:'asgytdfats01',name:'英雄联盟'},{id:'asgytdfats02',name:'王者农药'},{id:'asgytdfats03',name:'红色警戒'},{id:'asgytdfats04',name:'斗罗大陆'}])let imgUrl = ref('https://z1.ax1x.com/2023/11/19/piNxLo4.jpg')let videoUrl = ref('http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4')</script>

子组件

<template><div class="category"><h2>{{title}}</h2><slot>默认内容</slot></div>
</template><script setup lang="ts" name="Category">defineProps(['title'])
</script>

5、具名插槽 

用法:

(1)父组件 用template v-slot:s1或者#s2来命名

(2)子组件 用slot name=xxx来决定存放位置

这样父组件里面的数据就能决定放在子组件的哪个位置了,不受顺序影响

父组件中:<Category title="今日热门游戏"><template v-slot:s1><ul><li v-for="g in games" :key="g.id">{{ g.name }}</li></ul></template><template #s2><a href="">更多</a></template></Category>
子组件中:<template><div class="item"><h3>{{ title }}</h3><slot name="s1"></slot><slot name="s2"></slot></div></template>

 

二、子传父

1、props

  • 用法:

(1)先在父组件内部定义一个关于获取数据的方法getToy,为这个方法getToy绑定prop属性为函数sendToy。表示为  :sendToy="getToy"

(2)子组件definprops(['prop',....])接收,并绑定点击事件@click="sendToy(toy)"把要传的数据发送过去。

  • 注意:

(1)子传父prop是函数

(2)props绑定的数据是只读属性,不能进行修改

父组件Father.vue

<template><div class="father"><h3>父组件</h3><h4 v-show="toy">子给的玩具:{{ toy }}</h4><Child :sendToy="getToy"/></div>
</template><script setup lang="ts" name="Father">
// @ts-nocheckimport Child from './Child.vue'import {ref} from 'vue'// 数据let toy = ref('')// 方法function getToy(value:string){toy.value = value}
</script>

子组件Child.vue 

<template><div class="child"><h3>子组件</h3><button @click="sendToy(toy)">把玩具给父亲</button></div>
</template><script setup lang="ts" name="Child">
// @ts-nocheckimport {ref} from 'vue'// 数据let toy = ref('奥特曼')// 声明接收propsdefineProps(['sendToy'])
</script>

2、emit自定义事件通信

用法:

(1) 先在父组件内部的子组件绑定自定义事件@xxx,即为 @send-toy="saveToy",再定义自定义事件的回调saveToy

(2)子组件声明事件,用definEmits(['send-toy'])

(3)子组件绑定事件传递参数,@click="emit('send-toy',toy)"

Father.vue

<template><div class="father"><h3>父组件</h3><h4 v-show="toy">子给的玩具:{{ toy }}</h4><!-- 给子组件Child绑定事件 --><Child @send-toy="saveToy"/></div>
</template><script setup lang="ts" name="Father">
// @ts-nocheckimport Child from './Child.vue'import { ref } from "vue";// 数据let toy = ref('')// 用于保存传递过来的玩具function saveToy(value:string){console.log('saveToy',value)toy.value = value}
</script>

Child.vue 

<template><div class="child"><h3>子组件</h3><h4>玩具:{{ toy }}</h4><button @click="emit('send-toy',toy)">测试</button></div>
</template><script setup lang="ts" name="Child">
// @ts-nocheckimport { ref } from "vue";// 数据let toy = ref('奥特曼')// 声明事件const emit =  defineEmits(['send-toy'])
</script>

3、v-model 

4、$parent

用法:

(1)父组件,用definExpose向外暴露自己的数据

(2)子组件,通过$parent获取父组件实例,定义点击事件传入$parent 修改父组件的数据

父组件:

<template><div class="father"><h3>父组件</h3><h4>房产:{{ house }}</h4><button @click="changeToy">修改Child1的玩具</button><button @click="changeComputer">修改Child2的电脑</button><button @click="getAllChild($refs)">让所有孩子的书变多</button><Child1 ref="c1"/><Child2 ref="c2"/></div>
</template><script setup lang="ts" name="Father">import Child1 from './Child1.vue'import Child2 from './Child2.vue'import { ref,reactive } from "vue";let c1 = ref()let c2 = ref()// 注意点:当访问obj.c的时候,底层会自动读取value属性,因为c是在obj这个响应式对象中的/* let obj = reactive({a:1,b:2,c:ref(3)})let x = ref(4)console.log(obj.a)console.log(obj.b)console.log(obj.c)console.log(x) */// 数据let house = ref(4)// 方法function changeToy(){c1.value.toy = '小猪佩奇'}function changeComputer(){c2.value.computer = '华为'}function getAllChild(refs:{[key:string]:any}){console.log(refs)for (let key in refs){refs[key].book += 3}}// 向外部提供数据defineExpose({house})
</script>

子组件1:

<template><div class="child1"><h3>子组件1</h3><h4>玩具:{{ toy }}</h4><h4>书籍:{{ book }} 本</h4><button @click="minusHouse($parent)">干掉父亲的一套房产</button></div>
</template><script setup lang="ts" name="Child1">import { ref } from "vue";// 数据let toy = ref('奥特曼')let book = ref(3)// 方法function minusHouse(parent:any){parent.house -= 1}// 把数据交给外部defineExpose({toy,book})</script>

5、作用域插槽 

比如你要在父组件显示子组件的不同形式(有序,无序,标题),但是当你在父组件遍历子组件的数据时候,你是拿不到的。这时候就用到了作用域插槽。数据在子组件,数据的展示解构由父组件决定。

用法:

(1)父组件  使用template v-slot="params" params就是子组件传递过来的所有对象

(2)子组件 用props绑定数据,传给了内置组件slot,slot又把数据传给了他的使用者,父组件。

 父组件

<template><div class="father"><h3>父组件</h3><div class="content"><Game><template v-slot="params"><ul><li v-for="y in params.youxi" :key="y.id">{{ y.name }}</li></ul></template></Game><Game><template v-slot="params"><ol><li v-for="item in params.youxi" :key="item.id">{{ item.name }}</li></ol></template></Game><Game><template #default="{youxi}"><h3 v-for="g in youxi" :key="g.id">{{ g.name }}</h3></template></Game></div></div>
</template><script setup lang="ts" name="Father">import Game from './Game.vue'
</script>

子组件

<template><div class="game"><h2>游戏列表</h2><slot :youxi="games" x="哈哈" y="你好"></slot></div>
</template><script setup lang="ts" name="Game">import {reactive} from 'vue'let games = reactive([{id:'asgytdfats01',name:'英雄联盟'},{id:'asgytdfats02',name:'王者农药'},{id:'asgytdfats03',name:'红色警戒'},{id:'asgytdfats04',name:'斗罗大陆'}])
</script>

三、祖传孙

1、$attrs

用法:

(1)父组件 使用v-bind给数据绑定props

(2)子组件 不接收,使用v-bind="$attrs"存给孙组件

(3)孙组件 使用defineprops接收

父组件:

<template><div class="father"><h3>父组件</h3><h4>a:{{a}}</h4><h4>b:{{b}}</h4><h4>c:{{c}}</h4><h4>d:{{d}}</h4><Child :a="a" :b="b" :c="c" :d="d" /></div>
</template><script setup lang="ts" name="Father">import Child from './Child.vue'import {ref} from 'vue'let a = ref(1)let b = ref(2)let c = ref(3)let d = ref(4)</script>

子组件:

<template><div class="child"><h3>子组件</h3><GrandChild v-bind="$attrs"/></div>
</template><script setup lang="ts" name="Child">import GrandChild from './GrandChild.vue'
</script>

孙组件: 

<template><div class="grand-child"><h3>孙组件</h3><h4>a:{{ a }}</h4><h4>b:{{ b }}</h4><h4>c:{{ c }}</h4><h4>d:{{ d }}</h4></div>
</template><script setup lang="ts" name="GrandChild">defineProps(['a','b','c','d'])
</script>

2、provide inject

用法:

 (1)父组件,先从vue中import引入provide ,而后使用provide向后代提供数据

provide('名字',值)  

(2)后代组件,先import引入inject,而后使用inject接收数据,inject('名字','默认值')

 父组件:

<template><div class="father"><h3>父组件</h3><h4>银子:{{ money }}万元</h4><h4>车子:一辆{{car.brand}}车,价值{{car.price}}万元</h4><Child/></div>
</template><script setup lang="ts" name="Father">import Child from './Child.vue'import {ref,reactive,provide} from 'vue'let money = ref(100)let car = reactive({brand:'奔驰',price:100})function updateMoney(value:number){money.value -= value}// 向后代提供数据provide('moneyContext',{money,updateMoney})provide('car',car)

孙组件:

<template><div class="grand-child"><h3>我是孙组件</h3><h4>银子:{{ money }}</h4><h4>车子:一辆{{car.brand}}车,价值{{car.price}}万元</h4><button @click="updateMoney(6)">花爷爷的钱</button></div>
</template><script setup lang="ts" name="GrandChild">import { inject } from "vue";let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(param:number)=>{}})let car = inject('car',{brand:'未知',price:0})
</script>

四、孙传祖

1、$attrs

(1)父组件 绑定方法

(2)子组件 不接收使用$attrs传给孙组件

(3)孙组件 使用defineprops接收,并绑定点击事件

 父组件:

<template><div class="father"><h3>父组件</h3><h4>a:{{a}}</h4><h4>b:{{b}}</h4><h4>c:{{c}}</h4><h4>d:{{d}}</h4><Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/></div>
</template><script setup lang="ts" name="Father">import Child from './Child.vue'import {ref} from 'vue'let a = ref(1)let b = ref(2)let c = ref(3)let d = ref(4)function updateA(value:number){a.value += value}
</script>

子组件:

<template><div class="child"><h3>子组件</h3><GrandChild v-bind="$attrs"/></div>
</template><script setup lang="ts" name="Child">import GrandChild from './GrandChild.vue'
</script>

 孙组件:

<template><div class="grand-child"><h3>孙组件</h3><h4>a:{{ a }}</h4><h4>b:{{ b }}</h4><h4>c:{{ c }}</h4><h4>d:{{ d }}</h4><h4>x:{{ x }}</h4><h4>y:{{ y }}</h4><button @click="updateA(6)">点我将爷爷那的a更新</button></div>
</template><script setup lang="ts" name="GrandChild">defineProps(['a','b','c','d','x','y','updateA'])
</script>

2、provide inject

用法:

(1) 父组件,里面定义一个方法,在provide里面传的是money的值和这个方法给孙组件

(2)孙组件,可以把传过来的值和方法 利用解构赋值和inject接收,然后再添加一个按钮绑定这个方法,并传数据。就实现了孙传祖。

父组件:

<template><div class="father"><h3>父组件</h3><h4>银子:{{ money }}万元</h4><h4>车子:一辆{{car.brand}}车,价值{{car.price}}万元</h4><Child/></div>
</template><script setup lang="ts" name="Father">import Child from './Child.vue'import {ref,reactive,provide} from 'vue'let money = ref(100)let car = reactive({brand:'奔驰',price:100})function updateMoney(value:number){money.value -= value}// 向后代提供数据provide('moneyContext',{money,updateMoney})provide('car',car)

孙组件:

<template><div class="grand-child"><h3>我是孙组件</h3><h4>银子:{{ money }}</h4><h4>车子:一辆{{car.brand}}车,价值{{car.price}}万元</h4><button @click="updateMoney(6)">花爷爷的钱</button></div>
</template><script setup lang="ts" name="GrandChild">import { inject } from "vue";let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(param:number)=>{}})let car = inject('car',{brand:'未知',price:0})
</script>

五、兄弟、任意组件

1、mitt任意组件通信

用法:

(1)提供数据的用emitter.emit触发事件。

@click="emitter.emit('send-toy',toy)">

(2)接收数据的用emitter.on绑定事件。

emitter.on('send-toy',(value:any)=>{

        toy.value = value

    })

Child1.vue

<template><div class="child1"><h3>子组件1</h3><h4>玩具:{{ toy }}</h4><button @click="emitter.emit('send-toy',toy)">玩具给弟弟</button></div>
</template><script setup lang="ts" name="Child1">
// @ts-nocheckimport {ref} from 'vue'import emitter from '@/utils/emitter';// 数据let toy = ref('奥特曼')
</script>

Child2.vue 

<template><div class="child2"><h3>子组件2</h3><h4>电脑:{{ computer }}</h4><h4>哥哥给的玩具:{{ toy }}</h4></div>
</template><script setup lang="ts" name="Child2">
// @ts-nocheckimport {ref,onUnmounted} from 'vue'import emitter from '@/utils/emitter';// 数据let computer = ref('联想')let toy = ref('')// 给emitter绑定send-toy事件emitter.on('send-toy',(value:any)=>{toy.value = value})// 在组件卸载时解绑send-toy事件onUnmounted(()=>{emitter.off('send-toy')})
</script>

2、pinia 

vue3组件常用的通信方式 Pinia_pinia实现组件的通信-CSDN博客

// 在 store.js 中定义 Pinia store
import { defineStore } from 'pinia';export const useCounterStore = defineStore({id: 'counter',state: () => ({count: 0,}),actions: {increment() {this.count++;},decrement() {this.count--;},},
});// 在组件中使用 store
<template><div><p>Count: {{ count }}</p><button @click="increment">Increment</button><button @click="decrement">Decrement</button></div>
</template><script>
import { useCounterStore } from './store';export default {setup() {const counterStore = useCounterStore();return {count: counterStore.count,increment: counterStore.increment,decrement: counterStore.decrement,};},
};
</script>

六、总结

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Hack The Box(黑客盒子)Dancing篇
  • 【区分vue2和vue3下的element UI ¶Upload 上传组件,分别详细介绍属性,事件,方法如何使用,并举例】
  • Apache Doris 基础 -- 数据表设计(使用AUTO_INCREMENT)
  • 水库大坝安全监测系统打通监控数据“最后一公里”
  • 黄仁勋:打破摩尔定律,机器人时代来了
  • Flink 入门案例介绍
  • Autoxjs 实践-Spring Boot 集成 WebSocket
  • NMF算法
  • el-input中change事件造成的坑
  • AI大数据处理与分析实战--体育问卷分析
  • 评价GPT-4的方案
  • blender从视频中动作捕捉,绑定到人物模型
  • 【调度算法】Boltzmann选择
  • Spring Boot 实现动态数据源配置
  • 嵌入式中C语言经典的面试题分享
  • [译] 怎样写一个基础的编译器
  • Bootstrap JS插件Alert源码分析
  • httpie使用详解
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • Mocha测试初探
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • Spring核心 Bean的高级装配
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 入口文件开始,分析Vue源码实现
  • 微信公众号开发小记——5.python微信红包
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • ​520就是要宠粉,你的心头书我买单
  • ​比特币大跌的 2 个原因
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #Linux(帮助手册)
  • %@ page import=%的用法
  • ()、[]、{}、(())、[[]]命令替换
  • (2)nginx 安装、启停
  • (arch)linux 转换文件编码格式
  • (Charles)如何抓取手机http的报文
  • (SERIES12)DM性能优化
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (顺序)容器的好伴侣 --- 容器适配器
  • (转)visual stdio 书签功能介绍
  • (转)可以带来幸福的一本书
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • *Algs4-1.5.25随机网格的倍率测试-(未读懂题)
  • .env.development、.env.production、.env.staging
  • .Mobi域名介绍
  • .NET Micro Framework 4.2 beta 源码探析
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .Net小白的大学四年,内含面经