0


Vue3父子组件间传参通信

Vue3 父子组件间通信

前言

本文主要是记录Vue3在setup语法糖下的父子组件间传参的四种方式

Vue3+TypeScript

一、父传子 defineProps

父组件传值给子组件主要是由父组件为子组件通过v-bind绑定数值,而后传给子组件;子组件则通过defineProps接收使用。

如下为父组件

  1. Father.vue
  1. <template><div class="fa"><div style="margin: 10px;">我是父组件</div><Son :fatherMessage="fatherMessage"></Son></div></template><script setup lang="ts">import Son from'./Son.vue'import{ref}from"vue";const fatherMessage = ref<string>("我是父组件传过来的值")</script><style scoped>.fa{border: 3px solid cornflowerblue;width: 400px;
  2. text-align: center;}</style>

如下为子组件Son.vue

  1. <template><div style="margin: 10px;border: 2px solid red">
  2. 我是子组件
  3. <div style="margin: 5px;border: 2px solid gold">
  4. 父组件传值接收区:{{fatherMessage}}</div></div></template><script setup lang="ts">interfaceProps{
  5. fatherMessage?: string,}
  6. defineProps<Props>()</script>

父组件

  1. Father.vue

中在调用

  1. Son.vue

这个子组件时,使用

  1. v-bind

绑定参数

  1. fatherMessage

,并传给

  1. Son.vue

子组件

  1. Son.vue

使用

  1. defineProps

接收

  1. fatherMessage

这个参数,而后就可以正常使用该参数。

二、子传父 defineEmits

子组件传值给父组件主要是子组件通过

  1. defineEmits

注册一个自定义事件,而后触发

  1. emit

去调用该自定义事件,并传递参数给父组件。

在父组件中调用子组件时,通过

  1. v-on

绑定一个函数,通过该函数获取传过来的值。

如下为子组件

  1. Son.vue
  1. <template><div style="margin: 10px;border: 2px solid red">
  2. 我是子组件
  3. <button @click="transValue" style="margin: 5px">传值给父组件</button></div></template><script setup lang="ts">import{ref}from"vue";// 定义所要传给父组件的值const value = ref<String>("我是子组件传给父组件的值")// 使用defineEmits注册一个自定义事件const emit =defineEmits(["getValue"])// 点击事件触发emit,去调用我们注册的自定义事件getValue,并传递value参数至父组件consttransValue=()=>{emit("getValue", value.value)}</script>

如下为父组件

  1. Father.vue
  1. <template><div class="fa"><div style="margin: 10px;">我是父组件</div>
  2. 父组件接收子组件传的值:{{sonMessage}}<Son @getValue="getSonValue"></Son></div></template><script setup lang="ts">import Son from'./Son.vue'import{ref}from"vue";const sonMessage = ref<string>("")constgetSonValue=(value: string)=>{
  3. sonMessage.value = value
  4. }</script><style scoped>.fa{border: 3px solid cornflowerblue;width: 400px;
  5. text-align: center;}</style>

父组件

  1. Father.vue

中在调用

  1. Son.vue

这个子组件时,当子组件

  1. Son.vue

需要传参给父组件

  1. Father.vue

时,使用

  1. defineEmits

注册一个事件

  1. getValue

,而后设置点击事件

  1. transValue

去触发

  1. emit

,去调用我们注册的自定义事件

  1. getValue

,并传递

  1. value

参数至父组件。

父组件

  1. Father.vue

在获取子组件

  1. Son.vue

传过来的值时,通过在子组件上使用

  1. v-on

设置响应函数

  1. getValue

**(该函数与子组件中的注册自定义事件

  1. getValue

名称需一致)**,并绑定一个函数

  1. getSonValue

来获取传过来的值。

三、子组件暴露属性给父组件 defineExpose

当父组件想直接调用父组件的属性或者方法时,子组件可以使用

  1. defineExpose

暴露自身的属性或者方法,父组件中使用

  1. ref

调用子组件暴露的属性或方法。
如下为子组件

  1. Son.vue
  1. <template><div style="margin: 10px;border: 2px solid red">
  2. 我是子组件
  3. </div></template><script setup lang="ts">import{ref, defineExpose}from"vue";// 暴露给父组件的值const toFatherValue = ref<string>("我是要暴露给父组件的值")// 暴露给父组件的方法consttoFatherMethod=()=>{
  4. console.log("我是要暴露给父组件的方法")}// 暴露方法和属性给父组件defineExpose({toFatherMethod, toFatherValue})</script>

如下为父组件

  1. Father.vue
  1. <template><div class="fa"><div style="margin: 10px;">我是父组件</div><button @click="getSonMethod">获取子组件的方法</button><Son ref="sonMethodRef"></Son></div></template><script setup lang="ts">import Son from'./Son.vue'import{ref}from"vue";const sonMethodRef =ref()constgetSonMethod=()=>{
  2. sonMethodRef.value.toFatherMethod()
  3. console.log(sonMethodRef.value.toFatherValue)}</script><style scoped>.fa{border: 3px solid cornflowerblue;width: 400px;
  4. text-align: center;}</style>

在子组件中定义属性

  1. toFatherValue

和方法

  1. toFatherMethod

,而后通过

  1. defineExpose

暴露出来。
父组件调用时,为子组件绑定一个

  1. ref

,并定义一个

  1. ref

变量

  1. sonMethodRef

,通过调用

  1. sonMethodRef

,来获取子组件暴露出来的属性和方法。

四、依赖注入Provide / Inject

从上面的介绍里我们可以了解到父子组件之间的通信,但是却存在这样的情况:有一些多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用 props 则必须将其沿着组件链逐级传递下去,这会非常麻烦:
在这里插入图片描述

虽然这里的

  1. Footer

组件可能根本不关心这些

  1. props

,但为了使

  1. DeepChild

能访问到它们,仍然需要定义并向下传递。如果组件链路非常长,可能会影响到更多这条路上的组件。这一问题被称为“prop 逐级透传”,显然是我们希望尽量避免的情况。

  1. provide

  1. inject

可以帮助我们解决这一问题。 一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。
在这里插入图片描述
如下为父组件

  1. Root.vue
  1. <template><div>
  2. 我是root组件
  3. <Footer></Footer></div></template><script setup lang="ts">import{ provide, ref }from'vue'import Footer from'./Footer.vue'const toChildValue= ref<string>("我是给所有子组件的值")// 将toChildValue注入到所有子组件中provide(/* 注入名 */'toChildValue',/* 值 */ toChildValue)</script>

如下为子组件

  1. Footer.vue
  1. <template><div>
  2. 我是footer组件
  3. <div>
  4. 接收父组件的值:{{getFatherValue}}</div><DeepChild></DeepChild></div></template><script setup lang="ts">import DeepChild from"./DeepChild.vue"import{ref,inject,Ref}from"vue";// 获取父组件提供的值// 如果没有祖先组件提供 "toChildValue"// ref("") 会是 "这是默认值"const getFatherValue = inject<Ref<string>>(/* 注入名 */"toChildValue",/* 默认值 */ref(""))</script>

如下为孙子组件

  1. DeepChild.vue
  1. <template><div>
  2. 我是deepChild组件
  3. <div>
  4. 接收爷爷组件的值:{{getGrandFatherValue}}
  5. </div></div></template><scriptsetuplang="ts">import{inject, ref, Ref}from"vue";// 获取爷爷组件提供的值// 如果没有爷爷组件提供 "toChildValue"// value 会是 ""const getGrandFatherValue = inject<Ref<string>>(/* 注入名 */"toChildValue",/* 默认值 */ref(""))</script>

当最顶层的组件

  1. Root.vue

传值给所有子组件时,使用

  1. provide

进行注入

  1. provide(/* 注入名 */'toChildValue',/* 值 */ toChildValue)

而后无论哪个子组件想要获取

  1. toChildValue

的值,只需使用

  1. inject

即可

  1. inject<Ref<string>>(/* 注入名 */"toChildValue",/* 默认值 */ref(""))

当提供 / 注入响应式的数据时,如果想改变数据时,建议尽可能将任何对响应式状态的变更都保持在供给方组件中,即根组件

  1. Root.vue

。这样可以确保所提供状态的声明和变更操作都内聚在同一个组件内,使其更容易维护。

有的时候,我们可能需要在注入方组件中更改数据。在这种情况下,我们推荐在供给方组件内声明并提供一个更改数据的方法函数:
如下为父组件

  1. Root.vue
  1. <template><div>
  2. 我是root组件
  3. <Footer></Footer></div></template><script setup lang="ts">import{InjectionKey, provide, Ref, ref}from'vue'import Footer from'./Footer.vue'const toChildValue= ref<string>("我是给所有子组件的值")/**
  4. * 修改父组件值的方法
  5. */constchangeValue=()=>{
  6. toChildValue.value ="我是父组件修改的值"}// 定义一个注入key的类型(建议将注入 key 的类型放在一个单独的文件中,这样它就可以被多个组件导入)interfaceProvideType{toChildValue: Ref<string>;changeValue:()=>void;}// 为注入值标记类型const toValue =Symbol()as InjectionKey<ProvideType>// 将toChildValue和changeValue注入到所有子组件中provide(/* 注入名 */'toValue',/* 值 */{
  7. toChildValue,
  8. changeValue
  9. })</script>
  1. provide

  1. inject

通常会在不同的组件中运行。要正确地为注入的值标记类型,Vue 提供了一个

  1. InjectionKey

接口,它是一个继承自

  1. Symbol

的泛型类型,可以用来在提供者和消费者之间同步注入值的类型。
建议将注入

  1. key

的类型放在一个单独的文件中,这样它就可以被多个组件导入。

  1. // 定义一个注入key的类型//(建议将注入 key 的类型放在一个单独的文件中,这样它就可以被多个组件导入)interfaceProvideType{toChildValue: Ref<string>;changeValue:()=>void;}// 为注入值标记类型const toValue =Symbol()as InjectionKey<ProvideType>

如下为孙子组件

  1. DeepChild.vue
  1. <template><div>
  2. 我是deepChild组件
  3. <div><button @click="changeValue">改变祖先组件的值</button>{{toChildValue}}</div></div></template><script setup lang="ts">import{inject, ref, Ref}from"vue";// 定义注入值的类型interfaceProvideType{toChildValue: Ref<string>;changeValue:()=>void;}// 解构获取父组件传的值,需要进行强制类型转换const{toChildValue, changeValue}=inject(/* 注入名 */"toValue")as ProvideType
  4. // 不解构时,只需指定类型即可// const value = inject<ProvideType>(/* 注入名 */"toValue")</script>

当祖先组件提供参数与方法时,子组件在解构时需要强制转换该值的类型

  1. // 解构获取父组件传的值const{toChildValue, changeValue}=inject(/* 注入名 */"toValue")as ProvideType

如果子组件在使用时不进行解构,则直接指明类型即可

  1. // 不解构时,直接指定类型即可const value = inject<ProvideType>(/* 注入名 */"toValue")

参考

1、小满ZS 学习Vue3 第二十三章(依赖注入Provide / Inject) https://blog.csdn.net/qq1195566313/article/details/123143981?spm=1001.2014.3001.5501
2、Vue3官网 依赖注入
https://cn.vuejs.org/guide/components/provide-inject.html


本文转载自: https://blog.csdn.net/qq_45397526/article/details/126281133
版权归原作者 不会写代码的小周 所有, 如有侵权,请联系我们删除。

“Vue3父子组件间传参通信”的评论:

还没有评论