Vue3的组件通信汇总

news/2024/10/20 11:25:58/

目录

自定义属性props(父向子)

自定义事件(子向父)

ref获取子组件实例

$parent获取父组件实例

作用域插槽向父组件传值

兄弟组件传值

父子双向数据绑定

useAttrs获取父组件传来的属性和事件

深层组件传值

Pinia实现任意组件之间通信


  • 首先,我们要明白,数据流是自顶向下的,也就是说,只能上层的修改下层的,不能下层的修改上层的(正确的做法,应该是下层的通知上层的,让上层的去修改)

自定义属性props(父向子)

  • 可以用于父向子传值
  • vue3中,子组件用 defineProps这个hook来接收父组件传来的数据。可以是一个数组,也可以是一个对象

  • defineProps不需要导入,defineProps接收到的数据,可以直接进行使用

父组件

<template><div class="father"><h1>father</h1><Son :count="count"></Son></div>
</template><script setup>
import { ref } from 'vue'
import Son from './Son.vue'const count = ref(100)
</script><style lang="scss" scoped>
.father {height: 300px;background-color: pink;padding: 20px;
}
</style>

子组件

<template><div class="son"><h3>son</h3><p>父组件传来的数据: {{ count }}</p></div>
</template><script setup>
// 1. 数组
// const props = defineProps(['count'])
// 2. 对象,可以对父组件传来的数据进行一下校验
const props = defineProps({count: {type: Number,default: 0,},
})
</script><style lang="scss" scoped>
.son {height: 100px;background-color: #91beff;
}
</style>

自定义事件(子向父)

可以用于子向父传值

  1. 子组件通过emit向父组件传递一个自定义事件

  2. 父组件,在子组件标签上接收自定义事件,并声明处理函数

子组件

<template><div class="son"><h3>son</h3><button @click="sendData">点击向父组件传值</button></div>
</template><script setup>
// 子组件通过 defineEmits 这个hook声明自定义事件
const $emit = defineEmits(['sendData'])const sendData = () => {// 1. 子组件通过 emit 向父组件传递一个自定义事件(第一个参数是自定义事件名,第二个参数是传递的参数)$emit('sendData', 999)
}
</script><style lang="scss" scoped>
.son {height: 100px;background-color: #91beff;
}
</style>

父组件

<template><div class="father"><h1>father --- 接收到的子组件传过来的值:{{ count }}</h1><!-- 2. 父组件,在子组件标签上接收自定义事件,并声明处理函数 --><Son @sendData="sendData"></Son></div>
</template><script setup>
import { ref } from 'vue'
import Son from './Son.vue'let count = ref(0)// 3. 处理函数,如果子组件传递了参数,就可以写形参进行接收
const sendData = (val) => {console.log(val) // 接收到的子组件的参数// 可以把参数转存到声明的变量中,这样就可以直接使用了count.value = val
}
</script><style lang="scss" scoped>
.father {height: 300px;background-color: pink;padding: 20px;
}
</style>

补充:vue2和3的自定义事件

  • vue2中,如果在组件标签上写clickmouseEnterfocus等等这些原生的事件,它会以为就是自定义事件,不会以为是原生事件。如果想要它以原生的事件执行,则需要添加.native修饰符

<组件名 @click.native = 'handleClick'></组件名>

  • vue3,如果绑定原生事件,它就会以为就是原生事件。只要在子组件内,条件满足就会触发

<组件名 @click = 'handleClick'></组件名>

ref获取子组件实例

  • ref 可以获取真实的DOM节点,也可以获取组件实例。获取子组件实例后,就可以通过子组件实例,直接修改和使用子组件定义的变量,也可以直接调用子组件的方法

  • 组件内部的数据是封闭保护的,如果向让外界使用,可以使用defineExpose这个hook进行暴露

父组件

<template><main class="father"><h1>father</h1><Son ref="son"></Son></main>
</template><script setup>
import { onMounted, ref } from 'vue'
import Son from './Son.vue'// 子组件的实例化
let son = ref()
// 只有挂载到父组件之后,才能使用子组件的属性和方法
onMounted(() => {console.log(son.value.num)
})
</script><style>
.father {height: 400px;background-color: pink;padding: 20px;
}
</style>

子组件

<template><div class="son"><h2>son</h2></div>
</template><script setup>
import { ref } from 'vue'let num = ref(10)
// 需要使用 defineExpose 进行对外暴露(方法和属性都可以)
defineExpose({num,
})
</script><style>
.son {height: 200px;background-color: skyblue;
}
</style>

$parent获取父组件实例

  • 可以在子组件中通过$parentz这个参数获取父组件的实例

  • 组件内部的数据是封闭保护的,如果向让外界使用,可以使用defineExpose这个hook进行暴露

子组件

<template><div class="son"><h2>son</h2><button @click="getParentData($parent)">获取父组件的数据</button></div>
</template><script setup>
const getParentData = (parent) => {console.log('获取到了父组件实例', parent)
}
</script><style>
.son {height: 200px;background-color: skyblue;
}
</style>

父组件

<template><main class="father"><h1>father</h1><Son></Son></main>
</template><script setup>
import { ref } from 'vue'
import Son from './Son.vue'let count = ref(100)// 需要使用 defineExpose 进行对外暴露(方法和属性都可以)
defineExpose({count,
})
</script><style>
.father {height: 400px;background-color: pink;padding: 20px;
}
</style>

作用域插槽向父组件传值

  • 子组件可以利用作用域插槽向父组件传值

  • 作用域插槽就是可以传递数据的插槽,子组件可以将数据回传给父组件,父组件可以决定这些回传的数据,是以何种结构或者外观在子组件内部去展示

子组件

<template><div class="son"><h2>son</h2><p>--------默认插槽---------</p><slot></slot><p>--------具名插槽。name指定插槽的名字---------</p><slot name="superman"></slot><p>--------作用域插槽。通过属性绑定,向父组件提供数据---------</p><slot name="dc" :list="list"></slot></div>
</template><script setup lang="ts">
import { ref } from 'vue'const list = ref([{name: '张三',age: 21,color: 'pink'},{name: '里斯',age: 22,color: 'skyblue'},{name: '王五',age: 23,color: 'hotpink'},
])
</script><style>
.son {height: 150px;background-color: skyblue;
}
</style>

父组件

<template><main class="father"><h1>father</h1><Son><p>默认插槽里面的内容</p><template #superman>具名插槽中的内容</template><template #dc="{ list }"><ul><li v-for="i in list" :key="i.name" :style="{ backgroundColor: i.color }">{{ i.name }} -- {{ i.age }}</li></ul></template></Son></main>
</template><script setup lang="ts">
import Son from './Son.vue'
</script><style>
.father {height: 300px;background-color: pink;padding: 20px;
}
</style>

兄弟组件传值

可以利用第三方插件 mitt 实现

首先进行安装

yarn add mitt

使用:

  1. src目录下创建一个bus的文件夹,里面创建一个mitt.js的文件。(文件名随便起)

import mitt from 'mitt'const emitter = mitt()export default emitter
  1. 发送方:导入mitt.js并用emit发送数据

<template><div class="son"><h3>son</h3><button @click="givingGifts">点我,给妹妹送一件礼物</button></div>
</template><script setup>
import emitter from '@/bus/mitt'const givingGifts = () => {// 发送方 emitter.emit('事件名', 参数)emitter.emit('foo', { car: 'QQ车' })
}
</script><style lang="scss" scoped>
.son {height: 100px;background-color: #91beff;
}
</style>
  1. 接收方:导入mitt.js并用on接收兄弟组件发来的数据

<template><div class="sister"><h2>sister</h2><p>接收到了哥哥的礼物:{{ gifts }}</p></div>
</template><script setup>
import { ref } from 'vue'
import emitter from '@/bus/mitt'let gifts = ref()// 接收方  emitter.on('事件名', (参数) => { ... })
emitter.on('foo', (val) => {gifts.value = val.car
})
</script><style lang="scss" scoped>
.sister {height: 100px;background-color: #91beff;
}
</style>

父子双向数据绑定

使用 v- model 进行父子组件之间的双向数据绑定

  • v-model不光用在表单上,也可以用在组件上。用在组件上绑定值后,子组件只需要defineProps接收即可

  • v-model用在组件上,默认就是v-model:modelValue,也就是默认绑定modelValue

  • 组件上可以通过v-model传递多个值

父组件

<template><main class="father"><h1>father</h1><Son v-model="count" v-model:str="str"></Son></main>
</template><script setup lang="ts">
import { ref } from 'vue'
import Son from './son.vue'let count = ref(100)
let str = ref('str')</script><style>
.father {height: 300px;background-color: pink;padding: 20px;
}
</style>

子组件

<template><div class="son"><h2>son</h2><p>接收到的父组件传来的值:{{ modelValue }} -- {{ str }}</p></div>
</template><script setup lang="ts">
const props = defineProps({modelValue: {type: Number},str: String
})
</script><style>
.son {height: 100px;background-color: skyblue;
}
</style>

这只是单纯的父向子传递了数据,如果子组件想要修改,则还需要向父组件发送自定义事件,通知父组件进行修改

实现父子通信

父组件


<template><main class="father"><h1>father</h1><Son v-model="count" v-model:str="str" @changeCount="changeCount"></Son></main>
</template><script setup lang="ts">
import { ref } from 'vue'
import Son from './son.vue'let count = ref(100)
let str = ref('str')const changeCount = (val: number) => {count.value = count.value + val
}</script><style>
.father {height: 400px;background-color: pink;padding: 20px;
}
</style>

子组件


<template><div class="son"><h2>son</h2><p>接收到的父组件传来的值:{{ modelValue }} -- {{ str }}</p><button @click="changeCount">修改父组件传来的数值</button></div>
</template><script setup lang="ts">
const props = defineProps({modelValue: {type: Number},str: String
})const $emits = defineEmits(['changeCount'])const changeCount = () => {$emits('changeCount',1)
}
</script><style>
.son {height: 200px;background-color: skyblue;
}
</style>

useAttrs获取父组件传来的属性和事件

  • 可以获取到父组件传递过来的属性和事件

  • 没有被definedProps声明接收的,都会被useAttrs兜底接收

父组件

<template><main class="father"><h1>father</h1><Son type="primary" size="small"></Son></main>
</template><script setup>
import Son from './Son.vue'
</script><style>
.father {height: 400px;background-color: pink;padding: 20px;
}
</style>

子组件

<template><div class="son"><h2>son</h2></div>
</template><script setup>
import { useAttrs } from 'vue'let $attrs = useAttrs()console.log($attrs)
</script><style>
.son {height: 200px;background-color: skyblue;
}
</style>

封装第三方的组件时

  • 可以用v-bind绑定$attrs对象。多用于对第三方ui库的组件进行二次开发

<el-button :='$attrs'><slot></slot>    
</el-button>

也可以接收事件

<Son type="primary" size="small" @click="clickSon"></Son>

深层组件传值

深层组件,就是有很多层的嵌套关系。类似于祖先和子孙的关系

使用 provide  和  inject  进行深层组件的传值

  • 祖先组件通过provide这个hook向子孙组件提供数据。它下面的所有子孙组件都可以使用提供的数据

  • 子孙组件通过inject这个hook接收祖先提供的数据

祖先组件

<template><main class="father"><h1>father</h1><Son></Son></main>
</template><script setup lang="ts">
import { ref,provide } from 'vue'
import Son from './son.vue'let count = ref(100)// 提供者:向子孙组件提供数据
provide('count',count)
</script><style>
.father {height: 300px;background-color: pink;padding: 20px;
}
</style>

中间组件

<template><div class="son"><h2>son</h2><GrandSon></GrandSon></div>
</template><script setup lang="ts">
import GrandSon from './GrandSon.vue';
</script><style>
.son {height: 100px;background-color: skyblue;
}
</style>

子孙组件

<template><div class="son"><h3>grandSon</h3><p>从祖先组件接收过来的数据:{{ count }}</p></div>
</template><script setup lang="ts">
import { inject } from 'vue'let count = inject('count')
</script><style>
.son {height: 100px;background-color: skyblue;
}
</style>

Pinia实现任意组件之间通信

  • 核心:stateactionsgetters

选项式用法

定义:

import { defineStore } from 'pinia'let userInfoStore = defineStore('user', {state: () => {return {name: '张三',}},getters: {},actions: {changeName(name: String) {this.name = name},},
})// 对外暴露
export default userInfoStore

使用:

<template><main><p>从数据仓库获取到的数据:{{ userInfo.name }}</p><button @click="changeName">修改数据仓库提供的name的值</button></main>
</template><script setup lang="ts">
import userInfoStore from '@/stores/counter'const userInfo = userInfoStore()const changeName = () => {// 1. 直接进行修改// userInfo.name = '李四'// 2. 通过 $patch 方法进行调用// userInfo.$patch({//   name: '李四',// })// 3. 通过仓库调用自身的方法去修改仓库的数据userInfo.changeName('李四')
}
</script>

组合式用法

定义:

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'const useCounterStore = defineStore('counter', () => {const count = ref(0)const doubleCount = computed(() => count.value * 2)function increment() {count.value++}return { count, doubleCount, increment }
})export default useCounterStore

使用:

<template><main><h1>首页</h1><p>数据仓库提供的数据:{{ useCounter.count }} -- {{ useCounter.doubleCount }}</p><button @click="changeCount">使用数据仓库提供的方法改变count值</button></main>
</template><script setup lang="ts">
import useCounterStore from '@/stores/counter'const useCounter = useCounterStore()const changeCount = () => {useCounter.increment()
}
</script>


http://www.ppmy.cn/news/96585.html

相关文章

ChatGPT桌面客户端支持gpt4模型,附使用说明

#软件核心功能&#xff1a; 1、支持OpenAI官方秘钥及API2D双秘钥使用&#xff1b;如果全局魔法&#xff0c;可以自己用官方秘钥&#xff1b;没魔法国内可直接使用API2D秘钥&#xff1b; 2、内置GPT4模型选项&#xff0c;如果你的官方秘钥支持可直接使用&#xff1b;你也可以注册…

【JavaScript】文件分片上传

文章目录 普通文件上传分片上传整体流程技术点分析文件选择方式隐藏input框&#xff0c;自定义trigger拖拽上传 分片动态分片 计算哈希workerrequestIdleCallback抽样 请求并发控制进度展示手动中止/暂停 合并流式并发合并 反思分片命名问题并发控制代码实现的问题 参考文献 普…

驱动开发:内核实现进程汇编与反汇编

在笔者上一篇文章《驱动开发&#xff1a;内核MDL读写进程内存》简单介绍了如何通过MDL映射的方式实现进程读写操作&#xff0c;本章将通过如上案例实现远程进程反汇编功能&#xff0c;此类功能也是ARK工具中最常见的功能之一&#xff0c;通常此类功能的实现分为两部分&#xff…

Solidity基础八

别慌&#xff0c;月亮也在大海某处迷茫 目录 一、Solidity 编程风格 1. 代码布局 2. 代码中各部分的顺序 3. 命名约定 二、Solidity 智能合约编写过程 1. solidity Hello World 2. 版本声明 3. 导入声明 4. 合约声明 三、Solidity 合约结构 智能合约 Test 四、So…

Android 12.0状态栏居中显示时间和修改时间显示样式

1.概述 在12.0的系统rom定制化开发中,在systemui状态栏系统时间默认显示在左边和通知显示在一起,但是客户想修改显示位置,想显示在中间,所以就要修改SystemUI 的Clock.java 文件这个就是管理显示时间的,居中显示的话就得修改布局文件了 效果图如下: 在这里插入图片描述 …

谁能真正替代你?AI辅助编码工具深度对比(chatGPT/Copilot/Cursor/New Bing)

写在开头 这几个月AI相关新闻的火爆程度大家都已经看见了&#xff0c;作为一个被裹挟在AI时代浪潮中的程序员&#xff0c;在这几个月里我也是异常兴奋和焦虑。甚至都兴奋的不想拖更了。不仅仅兴奋于AI对于我们生产力的全面提升&#xff0c;也焦虑于Copilot等AI辅助编码工具&am…

android MutableLiveData与AndroidViewModel避坑小提示,Java

android MutableLiveData与AndroidViewModel避坑小提示&#xff0c;Java import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LifecycleOwner; import androidx.l…

《Linux0.11源码解读》理解(四) head之重新设置IDT/GDT

上节提到&#xff0c;现在cs:ip指向0地址&#xff0c;此处存储着作为操作系统核心代码的system模块&#xff0c;是由head.s和 main.c以及后面所有源代码文件编译链接而成。head.s(以下简称head)紧挨着main.c&#xff0c;我们先执行head。 重新设置内核栈 _pg_dir: _startup_3…