Parameter passing communication between Vue3 parent and child components

foreword

This article mainly records the four ways Vue3 passes parameters between parent and child components under the syntax sugar of setup

Vue3+TypeScript

1. Father-to-son defineProps

The parent component passes the value to the child component mainly by the parent component binding the value for the child component through v-bind, and then passing it to the child component; the child component receives and uses it through defineProps.

The following is the parent componentFather.vue

<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;
  text-align: center;
}
</style>

The following is the subcomponent Son.vue

<template>
  <div style="margin: 10px;border: 2px solid red">
    我是子组件
    <div style="margin: 5px;border: 2px solid gold">
      父组件传值接收区:{
    
    {
    
    fatherMessage}}
    </div>
  </div>
</template>

<script setup lang="ts">
interface Props {
    
    
  fatherMessage?: string,
}
defineProps<Props>()

</script>

Father.vueWhen calling this child component in the parent component Son.vue, use v-bindthe binding parameter fatherMessageand pass it toSon.vue

Subcomponents Son.vueuse definePropsto receive fatherMessagethis parameter, and then they can use this parameter normally.

Two, Child and Father defineEmits

The child component passes the value to the parent component mainly by defineEmitsregistering a custom event, and then triggering emitto call the custom event, and passing parameters to the parent component.

When calling the child component in the parent component, by v-onbinding a function, the passed value is obtained through the function.

The following are subcomponentsSon.vue

<template>
  <div style="margin: 10px;border: 2px solid red">
    我是子组件
    <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参数至父组件
const transValue = () => {
    
    
  emit("getValue", value.value)
}

</script>

The following is the parent componentFather.vue

<template>
  <div class="fa">
    <div style="margin: 10px;">我是父组件</div>
    父组件接收子组件传的值:{
    
    {
    
    sonMessage}}
    <Son @getValue="getSonValue"></Son>
  </div>
</template>

<script setup lang="ts">
import Son from './Son.vue'
import {
    
    ref} from "vue";

const sonMessage = ref<string>("")
const getSonValue = (value: string) => {
    
    
  sonMessage.value = value
}
</script>

<style scoped>
.fa{
    
    
  border: 3px solid cornflowerblue;
  width: 400px;
  text-align: center;
}
</style>

Father.vueWhen calling this child component in the parent component Son.vue, when the child component Son.vueneeds to pass parameters to the parent component Father.vue, defineEmitsregister an event getValue, then set the click event transValueto trigger emit, call the custom event we registered getValue, and pass valuethe parameters to the parent component.

When the parent component obtains the value passed by Father.vuethe child component , it uses the setting response function on the child component (the function must be consistent with the name of the registered custom event in the child component ) , and binds a function to obtain the passed value.Son.vuev-ongetValuegetValuegetSonValue

3. The child component exposes properties to the parent component defineExpose

When the parent component wants to directly call the properties or methods of the parent component, the child component can use defineExposethe property or method that exposes itself, and the parent component uses refthe property or method exposed by calling the child component.
The following are subcomponentsSon.vue

<template>
  <div style="margin: 10px;border: 2px solid red">
    我是子组件

  </div>
</template>

<script setup lang="ts">
import {
    
    ref, defineExpose} from "vue";

// 暴露给父组件的值
const toFatherValue = ref<string>("我是要暴露给父组件的值")

// 暴露给父组件的方法
const toFatherMethod = () => {
    
    
  console.log("我是要暴露给父组件的方法")
}
// 暴露方法和属性给父组件
defineExpose({
    
    toFatherMethod, toFatherValue})

</script>

The following is the parent componentFather.vue

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

const getSonMethod = () => {
    
    
  sonMethodRef.value.toFatherMethod()
  console.log(sonMethodRef.value.toFatherValue)
}

</script>

<style scoped>
.fa{
    
    
  border: 3px solid cornflowerblue;
  width: 400px;
  text-align: center;
}
</style>

toFatherValueDefine properties and methods in subcomponents toFatherMethod, and then defineExposeexpose them through.
When the parent component calls, bind one for the child component refand define a refvariable to get the properties and methods exposed by the child component sonMethodRefby calling .sonMethodRef

4. Dependency injection Provide / Inject

From the above introduction, we can understand the communication between parent and child components, but there is such a situation: there are some multi-level nested components, forming a huge component tree, and a deep subcomponent needs a Partial data in a distant ancestor component. In this case, if you only use props, you have to pass them down the component chain, which is very cumbersome:
insert image description here

Although Footerthe components may not care about these at all props, they still need to be defined and passed down in order to DeepChildenable access to them. If the component chain is very long, it may affect more components on this road. This problem is called "prop step-by-step transparent transmission" , and it is obviously a situation we want to avoid as much as possible.
provideand injectcan help us with this. A parent component acts as a dependency provider for all its descendants. Any descendant of the component tree, no matter how deep the hierarchy is, can inject the dependencies provided by the parent component for the entire chain.
insert image description here
The following is the parent componentRoot.vue

<template>
  <div>
    我是root组件
    <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>

The following are subcomponentsFooter.vue

<template>
  <div>
    我是footer组件
    <div>
      接收父组件的值:{
    
    {
    
    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>

The following is the grandchildren componentDeepChild.vue

<template>
  <div>
    我是deepChild组件
    <div>
      接收爷爷组件的值:{
   
   {getGrandFatherValue}}
    </div>
  </div>
</template>

<script setup lang="ts">
import {
      
      inject, ref, Ref} from "vue";

// 获取爷爷组件提供的值
// 如果没有爷爷组件提供 "toChildValue"
// value 会是 ""
const getGrandFatherValue = inject<Ref<string>>(/* 注入名 */"toChildValue",/* 默认值 */ ref(""))
</script>

When the topmost component Root.vuepasses values ​​to all child components, provideinject using

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

Then no matter which subcomponent wants to get toChildValuethe value, just use injectit

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

When providing/injecting reactive data, if you want to change the data, it is recommended to keep any changes to the reactive state as much as possible in the supplier component , the root component Root.vue. This ensures that both the declaration of the provided state and the mutating operations are cohesive within the same component, making it easier to maintain.

Sometimes, we may need to change data in the injector component. In this case, we recommend declaring and providing a method function to change data in the supplier component:
the parent component is as followsRoot.vue

<template>
  <div>
    我是root组件
    <Footer></Footer>
  </div>
</template>

<script setup lang="ts">
import {
    
    InjectionKey, provide, Ref, ref} from 'vue'
import Footer from './Footer.vue'

const toChildValue= ref<string>("我是给所有子组件的值")
/**
 * 修改父组件值的方法
 */
const changeValue = () => {
    
    
  toChildValue.value = "我是父组件修改的值"
}
// 定义一个注入key的类型(建议将注入 key 的类型放在一个单独的文件中,这样它就可以被多个组件导入)
interface ProvideType {
    
    
  toChildValue: Ref<string>;
  changeValue: () => void;
}
// 为注入值标记类型
const toValue = Symbol() as InjectionKey<ProvideType>
// 将toChildValue和changeValue注入到所有子组件中
provide(/* 注入名 */ 'toValue', /* 值 */{
    
    
  toChildValue,
  changeValue
})
</script>

provideand will injectusually run in different components. To correctly mark the type of the injected value, Vue provides an InjectionKeyinterface , which is a Symbolgeneric type inherited from that can be used to synchronize the type of the injected value between the provider and the consumer.
It is recommended to put keyinjected types in a separate file so that it can be imported by multiple components.

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

The following is the grandchildren componentDeepChild.vue

<template>
  <div>
    我是deepChild组件
    <div>
      <button @click="changeValue">改变祖先组件的值</button>
      {
    
    {
    
    toChildValue}}
    </div>
  </div>
</template>

<script setup lang="ts">
import {
    
    inject, ref, Ref} from "vue";

// 定义注入值的类型
interface ProvideType {
    
    
  toChildValue: Ref<string>;
  changeValue: () => void;
}
// 解构获取父组件传的值,需要进行强制类型转换
const {
    
    toChildValue, changeValue} = inject(/* 注入名 */"toValue") as ProvideType
// 不解构时,只需指定类型即可
// const value = inject<ProvideType>(/* 注入名 */"toValue")
</script>

When the ancestor component provides parameters and methods, the child component needs to cast the type of the value when deconstructing

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

If the subcomponent does not destructure when used, just specify the type directly

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

reference

1. Xiaoman ZS Learning Vue3 Chapter 23 (Dependency Injection Provide / Inject) https://blog.csdn.net/qq1195566313/article/details/123143981?spm=1001.2014.3001.5501
2. Vue3 official website dependency injection
https:/ /cn.vuejs.org/guide/components/provide-inject.html

Guess you like

Origin blog.csdn.net/qq_45397526/article/details/126281133