【组件通信】Vue组件通信的7中方法,你都知道吗?

230 阅读3分钟

Vue3组件通信

  • 父子组件之间的通信
  • 兄弟组件之间的通信
  • 祖孙与后代组件之间的通信
  • 非关系组件间之间的通信

image.png

  1. 通过 props 传递
  2. 通过 $emit 触发自定义事件
  3. 使用 ref
  4. EventBus
  5. $parent$root
  6. Provide 与 Inject
  7. Vuex或pinia

Props

  • 父—>子
  • 父组件在标签中使用子组件标签字面量进行数据传递
  • 子组件通过defineprops([])接受对应标签字面量

father.vue

<Child info="父组件传递的名称" :messageToChildren="message"></Child>

<script setup lang="ts">
//props:可以实现父子组件通信,props数据还是只读的!!!
import Child from "./Child.vue";
import { ref } from "vue";
let message = ref<string>("这是父组件传递给子组件的信息");
</script>

children.vue

<h1>子组件children</h1>
<h1>{{ props.info }}</h1>
<span>{{ props.messageToChildren }}</span>

<script setup lang="ts">
// 接受数据
let props = defineProps(["info", "messageToChildren"]);
</script>

$emit触发自定义事件

  • ==子——>父==
  • 子组件通过defineEmits触发自定义事件,$emit第二个参数为传递的数据
  • 父组件绑定监听器获取到子组件传递过来的参数

father.vue

<template>
  <div>
    <h1>这里是父组件</h1>
    <Event1 @childrenToFatherMessage="fatherGetChildrenMessage" />
  </div>
</template>

<script setup lang="ts">
//引入子组件
import Event1 from "./Event1.vue";

// 绑定事件,调用的事件就是自定义事件
// childrenToFatherMessage:自定义事件
// fatherGetChildrenMessage:触发的回调
// message1,message2对应子组件带来的两个参数
const fatherGetChildrenMessage = (message1: any, message2: any) => {
  console.log(message1, message2);
};
</script>

children.vue

<p>我是子组件1</p>
<button @click="changeChildren">点击我执行自定义事件children</button>

<script setup lang="ts">
let $emit = defineEmits(["childrenToFatherMessage"]);
const changeChildren = () => {
  // 这里的childrenToFatherMessage是事件类型,后面都为传入的数据
  $emit("childrenToFatherMessage", "子组件给父组件传递的数据1", "子组件给父组件传递的数据2");
};
</script>

Event-Bus

  • 兄弟间传值
  • 创建中央事件总栈EventBus
  • 兄弟组件通过$emit触发自定义事件,$emit第二个参数为传递的数值
  • 另一个兄弟组件通过$on监听自定义事件

插件mitt

mitt - npm (npmjs.com)

  • 下载引入mitt:pnpm i mitt -s

  • 注册使用

    // 引入mitt插件:mitt一个方法,方法执行会返回bus对象
    import mitt from 'mitt'
    const $bus = mitt()
    export default $bus
    

children01.vue

<h2>我是子组件01</h2>
<button @click="handler">点击我给02送一台法拉利</button>

<script setup lang="ts">
// 引入$bus对象
import $bus from '../../bus';
//点击按钮回调
const handler = ()=>{
  // 传入的数据是一个对象
  $bus.emit('car',{car:"法拉利"});
}
</script>

children02.vue

<div class="child1">
<h3>我是子组件02</h3>

<script setup lang="ts">
import $bus from "../../bus";
// 组合式API函数
import { onMounted } from "vue";
// 组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
onMounted(() => {
  // 第一个参数:即为事件类型  第二个参数:即为事件回调
  $bus.on("car", (car) => {
    console.log(car);
  });
});
</script>

$parent

$root或ref

  • ** 父子传值**
  • 通过共同祖辈$parent或者$root搭建通信桥梁
  • $parent:可以在子组件获取父组件的实例,作为传递参数
  • ref:在父组件获取子组件的实例

son.vue

<h3>我是子组件:曹植{{money}}</h3>

<script setup lang="ts">
import {ref} from 'vue';
// 儿子钱数
let money = ref(666);
const fly = ()=>{
  console.log('我可以飞');
}
// 组件内部数据对外关闭的,别人不能访问
// 如果想让外部访问需要通过defineExpose方法对外暴露
defineExpose({
  money,
  fly
})
</script>

son02.vue

<h1>我是闺女曹杰{{money}}</h1>
<button @click="handler($parent)">点击我爸爸给我10000元</button>

<script setup lang="ts">
import {ref} from 'vue';
//闺女钱数
let money = ref(999999);
//闺女按钮点击回调
const handler = ($parent)=>{
   money.value+=10000;
   $parent.money-=10000;
}
</script>

father.vue

<h1>我是父亲曹操:{{money}}</h1>
<button @click="handler">找我的儿子曹植借10元</button>
<Son ref="son"></Son>
<Dau></Dau>

<script setup lang="ts">
// ref:可以获取真实的DOM节点,可以获取到子组件实例VC
// $parent:可以在子组件内部获取到父组件的实例
// 引入子组件
import Son from './Son.vue'
import Dau from './Daughter.vue'
import {ref} from 'vue';
// 父组件钱数
let money = ref(100000000);
// 获取子组件的实例
let son = ref();
// 父组件内部按钮点击回调
const handler = ()=>{
   money.value+=10;
   // 儿子钱数减去10
   son.value.money-=10;
   son.value.fly();
}
// 对外暴露
defineExpose({
   money
})
</script>

Provide与inject

provide:用于提供后代组件注入的值

inject:用于声明要通过从上层提供方匹配并注入进当前组件的属性。

  • 在祖先组件注入祖先组件提供数据,需要的参数就是祖先需要的key
  • 如果后代需要使用通过inject进行注入

grandChild.vue

<h1>孙子组件</h1>
<!-- 一开始是获取祖先的car:法拉利 -->
<p>{{car}}</p>
<button @click="updateCar">更新数据</button>

<script setup lang="ts">
import {inject} from 'vue';
// 注入祖先组件提供数据
// 需要参数:即为祖先提供数据的key
let car = inject('TOKEN');
const updateCar = () =>{
   car.value  = '自行车';
}
</script>

children.vue

<h1>我是子组件1</h1>
<grandChild></grandChild>

<script setup lang="ts">
import grandChild from './GrandChild.vue';
</script>

provide-inject

<h1>Provide与Inject{{car}}</h1>
<Child></Child>

<script setup lang="ts">
import Child from "./Child.vue";
//vue3提供provide(提供)与inject(注入),可以实现隔辈组件传递数据
import { ref, provide } from "vue";
let car = ref("法拉利");
//祖先组件给后代组件提供数据
//两个参数:第一个参数就是提供的数据key
//第二个参数:祖先组件提供数据
provide("TOKEN", car);
</script>

vuex

image.png

  • 适用场景: 复杂关系的组件数据传递
  • Vuex作用相当于一个用来存储共享变量的容器
  • state用来存放共享变量的地方
  • getter,可以增加一个getter派生状态,(相当于store中的计算属性),用来获得共享变量的值
  • mutations用来存放修改state的方法。(在pinia中舍弃了mutations,组件变量的变动回直接回影响仓库存储的变量)
  • actions也是用来存放修改state的方法,不过action是在mutations的基础上进行。常用来做一些异步操作

Vuex生命周期:

  • 组件dispatch发送请求
  • 异步操作后对结果进行提交,有mutations小仓库进行变量的修改
  • 修改后最后的值存储在state当中
  • 结果最终通过组件呈现

也可以使用pinia,具体实现参照官方文档详细解释

小结

  • 父子关系的组件数据传递选择 props$emit进行传递,也可选择ref
  • 兄弟关系的组件数据传递可选择$bus,其次可以选择$parent进行传递
  • 祖先与后代组件数据传递可选择attrslisteners或者 ProvideInject
  • 复杂关系的组件数据传递可以通过vuex存放共享的变量