@[TOC] 父子组件通信

前言

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

Vue3+TypeScript

一、父传子— props

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

父组件: 父组件中给要传给子组件的属性type直接赋值

    <el-dialog :close-on-click-modal="false" :title="dialog.title" v-model="dialog.visible" width="1000px" top="15px"
      @close="cancel">
      <form-children   type="create" @changeVisible="handleVisible" ref="formChildrenRef"/>
    </el-dialog>

子组件: 子组件里的props定义从父组件拿过来的属性名type, 从而拿到父组件传过来的属性值

const props = defineProps({
    type: {
        default: 'edit',
        type: String,
    },

});

二、子传父— defineEmits

父组件: 拿到子组件传过来的数据false给handleVisible方法里

   <el-dialog :close-on-click-modal="false" :title="dialog.title" v-model="dialog.visible" width="1000px" top="15px"
   @close="cancel">
      <form-children   type="create" @changeVisible="handleVisible" ref="formChildrenRef"/>
   </el-dialog>

function handleVisible(data:any) {
  state.dialog.visible = data
}

子组件: 子组件中通过emit调用父组件中的changeVisible方法,给父组件的这个方法里传入数据false

   if (state.type == 'create') {
                        addScene(state.formData).then(() => {
                            ElMessage.success(state.type == 'edit' ? t('message.modifySuccess') : t('message.addSuccess'));
                            emit('changeVisible', false)
                            cancel();
                        });
   }

三、子组件暴露属性给父组件 defineExpose (父组件调用子组件的方法)

当父组件想直接使用子组件的属性或者方法时,子组件可以使用defineExpose暴露自身的属性或者方法。

父组件: 父组件通过ref定义子组件实例,通过调用实例获得子组件的属性和方法

  <el-dialog :close-on-click-modal="false" :title="dialog.title" v-model="dialog.visible" width="1000px" top="15px"
  @close="cancel">
      <form-children   type="create" @changeVisible="handleVisible" ref="formChildrenRef"/>
  </el-dialog>
    
//拿到子组件实例
const formChildrenRef = ref()


function cancel() {
  resetForm();
  state.dialog.visible = false;
  // 调用子组件的方法
  if(formChildrenRef.value) {
    formChildrenRef.value.resetForm()
  }
}

子组件: 子组件通过defineExpose暴露对象和方法

function resetForm() {
    state.actions = []
    state.conditions = []
    state.actionTags = []
    state.conditionsTags = []
    state.formData = {
        sceneName: '',
        sceneType: 1,
        identifierType: null,
        enable: 1,
    }
    dataFormRef.value.resetFields();
    i18nFormRef.value.formRef.resetFields();

}

defineExpose({
    resetForm
})

四、依赖注入Provide / Inject

在父子组件传递数据时,通常使用的是 props 和 emit,父传子时,使用的是 props,如果是父组件传孙组件时,就需要先传给子组件,子组件再传给孙组件,如果多个子组件或多个孙组件使用时,就需要传很多次,会很麻烦。

像这种情况,可以使用 provide 和 inject 解决这种问题,不论组件嵌套多深,父组件都可以为所有子组件或孙组件提供数据,父组件使用 provide 提供数据,子组件或孙组件 inject 注入数据。同时兄弟组件之间传值更方便。

如下为父组件Root.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>

如下为子组件Footer.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>

如下为孙子组件DeepChild.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>

当最顶层的组件Root.vue传值给所有子组件时,使用provide进行注入

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

而后无论哪个子组件想要获取toChildValue的值,只需使用inject即可

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

为什么父子组件传值时有时用props不行,用provide可以?

  1. 父组件通过props向子组件传递数据时,只能实现单向的数据流动,也就是说只能从父组件传递数据到子组件,而子组件无法直接修改父组件的数据。
  2. 而provide和inject是Vue提供的一种更加灵活的数据传递方式,可以实现从祖先组件向后代组件的跨层级传递数据,而且不受单向数据流的限制,后代组件可以直接修改祖先组件的数据。
  3. 这种方式更加适合非父子关系的组件通信,例如兄弟组件之间共享数据等。因此,使用provide和inject可以更加方便地实现组件之间的数据传递。
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐