首页 > 其他分享 >05 Vue3组件间的通信

05 Vue3组件间的通信

时间:2024-04-16 18:27:00浏览次数:21  
标签:vue name 05 let Vue3 组件 import ref

Vue3组件通信和Vue2的区别:

  • 移出事件总线,使用mitt代替。
  • vuex换成了pinia

  • .sync优化到了v-model里面了

  • $listeners所有的东西,合并到$attrs中了

  • $children被砍掉了

常见搭配形式
image

props - 【父传子 子传父】

父传子:属性值是非函数
子传父:属性值是函数
一般都用于 父传子

父传子

父组件

<template>
  <Child :car="car" :obj="obj" :list="list" />
</template>

<script lang="ts" setup>
import { reactive, ref } from 'vue';
import Child from '@/components/Child.vue';

const car = ref('奔驰');
// 定义数据类型-对象
type personsType = {
  id: string;
  name: string;
  age?: number;
};

// 定义数据类型-数组
// type ArrayPer = Array<persons>;
type ArrayPer = personsType[];

let obj = reactive<personsType>({ id: 'asdfj01', name: '张三' });

let list = reactive<ArrayPer>([
  { id: 'asdfj01', name: '张三', age: 10 },
  { id: 'asdfj02', name: '李四' },
  { id: 'asdfj03', name: '王五' },
]);
</script>

子组件-接收数据

<template>
  <h1>接收来自父组件的值:</h1>
  数值: {{ car }}
  <hr />
  对象:{{ obj }}
  <hr />
  数组:{{ list }}
  <hr />
</template>

<script lang="ts" setup>
import { defineProps } from 'vue';
// 定义数据类型-对象
type personsType = {
  id: string;
  name: string;
  age?: number;
};

// 方式一:直接接收
// defineProps(['car','obj','list']);

// 方式一:设置数据类型
defineProps<{
  car: string;
  obj: personsType;
  list: personsType[];
}>();
</script>

子传父

父组件

<template>
 # 子传父:属性值是 函数
  <Child :getMsg="getMsg" />
</template>

<script lang="ts" setup>
import Child from '@/components/Child.vue';

function getMsg(val: string) {
  console.log('收到了来自子组件的数据:' + val);
}
</script>

子组件

<template>
  <h1>点击按钮向父组件发送数据:</h1>
  <button @click="sentMsg">传送数据</button>
  <hr />
</template>

<script lang="ts" setup>
import { defineProps } from 'vue';

// 方式一:直接接收
let props = defineProps(['getMsg']);

// // 方式一:设置数据类型
// let props = defineProps<{
//   getMsg: Function;
// }>();

function sentMsg() {
  props.getMsg('哈哈哈');
}
</script>

自定义事件--【子传父】 defineEmits

父组件--接收数据

<template>
  // customerEventName 子组件里面的 自定义事件
  <Child @customerEventName="getMsg" />
</template>

<script lang="ts" setup>
import Child from '@/components/Child.vue';

function getMsg(val: string) {
  console.log('收到了来自子组件的数据:' + val);
}
</script>

子组件-发送数据

<template>
  <h1>点击按钮向父组件发送数据:</h1>
  <button @click="sentMsg">传送数据</button>
  <hr />
</template>

<script lang="ts" setup>
// customerEventName 是自定义事件的名称
const emit = defineEmits(['customerEventName']);
function sentMsg() {
  const msg = '哈哈哈';
  // 向父组件传递参数
  emit('customerEventName', msg);
}
</script>

mitt-- 任意组件间通信

与消息订阅与发布(pubsub)功能类似,可以实现任意组件间通信

1. 安装

npm i mitt

2. 构建 src\utils\emitter.ts

// 引入mitt
import mitt from 'mitt';

// 创建emitter
const emitter = mitt();

// 创建并暴露mitt
export default emitter;

3. mitt 绑定on-调用-发布-接收数据触发emit-定义-订阅-发数据 事件

// emitter表示的是 emitter对象


// 绑定事件--调用事件
emitter.on('abc', (value) => {
  console.log('abc事件被触发', value);
});
emitter.on('xyz', (value) => {
  console.log('xyz事件被触发', value);
});

setInterval(() => {
  // 触发事件--定义事件
  emitter.emit('abc', 666);
  emitter.emit('xyz', 777);
}, 1000);


setTimeout(() => {
  // 清理事件  
  emitter.all.clear();
  
  // 移除事件
  emitter.off('abc')
  
}, 3000);

4. 一个案例

父组件--监听事件--接收数据

<template>
  <Child />
</template>

<script lang="ts" setup>
import Child from '@/components/Child.vue';
import { onMounted, onUnmounted } from 'vue';
import mittBus from '@/utils/emitter';
onMounted(() => {
  getMsg();
});

onUnmounted(() => {
  mittBus.off('customerEventName');
});

const getMsg = () => {
  mittBus.on('customerEventName', (val) => {
    console.log('收到了来自子组件的数据:' + val);
  });
};
</script>

子组件--触发事件--发数据

<template>
  <h1>点击按钮向父组件发送数据:</h1>
  <button @click="sentMsg">传送数据</button>
  <hr />
</template>

<script lang="ts" setup>
import mittBus from '@/utils/emitter';

// customerEventName 是自定义事件的名称
const sentMsg = () => {
  const msg = '哈哈哈';
  // 向父组件传递参数msg
  mittBus.emit('customerEventName', msg);
};
</script>

v-model -- 父传子 子传父

v-model的双向数据绑定

v-model的双向数据绑定本质就是 input 绑定一个value值,同时监听input的value变化,重新赋值

<template>
  <h2>父组件</h2>
  <input type="text" v-model="username" />

  <!-- v-model的双向数据绑定本质就是 input 绑定一个value值,同时监听input的value变化,重新赋值 -->
  <input type="text" :value="username" @input="username = (<HTMLInputElement>$event.target).value" />
</template>

<script lang="ts" setup>
import { ref } from 'vue';
let username = ref('zhangsan');
</script>

组件标签上的v-model的本质

:moldeValueupdate:modelValue事件

  <!-- 通过 v-model 将username 传递给 Child 子组件 -->
 <Child v-model="username" />

 <!-- 组件标签上v-model的本质 -->
 <!-- 传递数据modelValue,同时绑定事件 update:modelValue -->
 <Child :modelValue="username" @update:modelValue="username = $event" />

父子互相通信

父组件--发送数据

<template>
  <h2>父组件</h2>
  <input type="text" v-model="username" />
  <!-- 通过 v-model 将username 传递给 Child 子组件 -->
  <Child v-model="username" />
</template>

<script lang="ts" setup>
import Child from '@/components/Child.vue';
import { ref } from 'vue';
let username = ref('zhangsan');
</script>


子组件-接收数据
通过 defineProps 接收 名为 modelValue 的数据,就是父组件传递过来的 username

<template>
  <hr />
  <h2>子组件</h2>
  <h3>通过v-model接收父组件的数据:</h3>
  {{ modelValue }}
  <input type="text" :value="modelValue" @input="emit('update:modelValue', (<HTMLInputElement>$event.target).value)" />
</template>

<script lang="ts" setup>
import { defineProps } from 'vue';
// 接收数据 modelValue
defineProps(['modelValue']);

// 自定义触发事件  pdate:modelValue
const emit = defineEmits(['update:modelValue']);
</script>

更换 modelValue 的命名

父组件

<template>
  <h2>父组件</h2>
  <input type="text" v-model="username" />
  <input type="text" v-model="pwd" />
  <!-- 通过 v-model 将username 传递给 Child 子组件 -->
  <Child v-model:abc="username" v-model:xyz="pwd" />
</template>

<script lang="ts" setup>
import Child from '@/components/Child.vue';
import { ref } from 'vue';
let username = ref('zhangsan');
let pwd = ref('12356');
</script>

子组件

<template>
  <hr />
  <h2>子组件</h2>
  <h3>通过v-model接收父组件的数据:</h3>
  {{ abc }}=={{ xyz }}
  <input type="text" :value="abc" @input="emit('update:abc', (<HTMLInputElement>$event.target).value)" />
  <input type="text" :value="xyz" @input="emit('update:xyz', (<HTMLInputElement>$event.target).value)" />
</template>

<script lang="ts" setup>
import { defineProps } from 'vue';
// 接收数据 modelValue
defineProps(['abc', 'xyz']);

// 自定义触发事件  pdate:modelValue
const emit = defineEmits(['update:abc', 'update:xyz']);
</script>

$attrs -- 祖孙间传递数据

$attrs 用于实现当前组件的父组件,向当前组件的子组件通信(祖→孙

$attrs是一个对象,包含所有父组件传入的标签属性

注意:$attrs会自动排除props中声明的属性(可以认为声明过的 props 被子组件自己“消费”了)

父组件

<template>
  <h2>父组件</h2>
  <h3>a={{ a }}</h3>
  <h3>b={{ b }}</h3>
  <h3>c={{ c }}</h3>
  <h3>num={{ num }}</h3>

  <Child :a="a" :b="b" :c="c" v-bind="{ x: 100, y: 200, z: 300 }" :updateSum="updateSum" />
</template>

<script lang="ts" setup>
import Child from '@/components/Child.vue';
import { ref } from 'vue';
let num = ref(1);
let a = ref('a');
let b = ref('b');
let c = ref('c');

const updateSum = (val: number) => {
  num.value = val;
};
</script>

子组件 : 子组件不做任何事情,直接将 $attrs 传递给孙子组件

<template>
  <hr />
  <h2>子组件不做任何事情,直接将 $attrs 传递给孙子组件</h2>
  <GrandChild v-bind="$attrs" />
</template>

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

孙组件

<template>
  <hr />
  <h2>孙子组件</h2>
  <h3>a={{ a }}</h3>
  <h3>b={{ b }}</h3>
  <h3>c={{ c }}</h3>
  <h3>x={{ x }}</h3>
  <h3>y={{ y }}</h3>
  <h3>z={{ z }}</h3>
  <button @click="updateSum(666)">点击将 num 的值 改为 666</button>
</template>

<script lang="ts" setup>
defineProps(['a', 'b', 'c', 'x', 'y', 'z', 'updateSum']);
</script>

$refs 父传子 $parent 子传父

$refs 父传子,结合ref组件标签

$refs 值为对象,包含所有被ref属性标识的DOM元素或组件实例

父组件:只可以修改 子组件向外暴露的数据

<template>
  <h2>父组件</h2>
  <!--  $refs 子组件所有的 实例对象  -->
  <button @click="getAllChilds($refs)">获取所有子组件的实例对象,并修改数据</button>
  <Child1 ref="c1" />
  <Child2 ref="c2" />
</template>

<script lang="ts" setup>
import Child1 from '@/components/Child1.vue';
import Child2 from '@/components/Child2.vue';
import { ref } from 'vue';
let c1 = ref();
let c2 = ref();

function getAllChilds(refs: any) {
  refs.c1.name = '张三三';
  refs.c2.name = '李四四';
}
</script>

子组件:必须向外暴露数据,父组件才可以修改和获取

子组件1

<template>
  <hr />
  <h2>子组件-Child1-{{ name }}</h2>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
let name = ref('张三');
// 将name向外暴露出去
defineExpose({ name });
</script>

子组件2

<template>
  <hr />
  <h2>子组件-Child2-{{ name }}</h2>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
let name = ref('李四');
// 将name向外暴露出去
defineExpose({ name });
</script>

$parent 子传父

$parent 值为对象,当前组件的父组件实例对象

父组件

<template>
  <h2>父组件</h2>
  数值num为=={{ num }}
  <Child1 />
</template>

<script lang="ts" setup>
import Child1 from '@/components/Child1.vue';
import { ref } from 'vue';
let num = ref(6);
// 必须将父组件的值暴露给子组件,子组件才可以修改父组件的值
defineExpose({ num });
</script>

子组件

<template>
  <hr />
  <h2>子组件-Child1</h2>
  <button @click="minus($parent)">将父组件num值减1</button>
</template>

<script lang="ts" setup>
function minus(p: any) {
  console.log(p.num);
  p.num -= 1;
}
</script>

provide、inject 祖孙间传递数据

Pinia

参考:
Pinia状态管理

插槽

参考:

标签:vue,name,05,let,Vue3,组件,import,ref
From: https://www.cnblogs.com/songxia/p/18137904

相关文章

  • 06 Vue3插槽
    6.9.【slot】1.默认插槽父组件中:<Categorytitle="今日热门游戏"><ul><liv-for="gingames":key="g.id">{{g.name}}</li></ul></Category>子组件中:......
  • 在宝塔上配置打包好的vue3项目
    配置文件如下server{listen80;server_namegongchang.365cb.cn; indexindex.htmlindex.htmdefault.phpdefault.htmdefault.html;root/www/wwwroot/dist;#SSL-STARTSSL相关配置,请勿删除或修改下一行带注释的404规则#error_page404/404.html;#SS......
  • 洛谷题单指南-数学基础问题-P1403 [AHOI2005] 约数研究
    原题链接:https://www.luogu.com.cn/problem/P1403题意解读:计算1~n每个数的约数个数之和。解题思路:1、数学方法1~n的约数范围也在1~n,要计算每个数的约数个数之和可以从约数出发,比如约数是x,那么在1~n中一共有n/x个数包含x这个约数x从1一直枚举到n,就可以得出每个约数是多少个......
  • Uni-app的Prompt组件实现
    代码实现<!--prompt组件--><template> <view> <viewv-show="show"class="uni-mask":style="{top:offsetTop+'px'}"@touchmove.stop.prevent="maskMoveHandle"></view> <view......
  • vue3中使用scss
    安装依赖npminstallsasssass-loader--save-dev局部使用<stylescopedlang="scss">...定义样式</style> 全局共享样式变量,在assets/style文件夹中定义mixin.scss文件,并设置一些样式;在其他文件使用定义的变量前需要引入样式文件表  在vite.config.ts文件中......
  • react组件地狱是什么怎么解决
    React中的“组件地狱”主要指的是在组件开发中,由于组件的过度嵌套或复杂的层次结构,导致代码变得难以阅读、理解和维护。这通常发生在开发者为了复用逻辑或状态而在组件中层层嵌套其他组件时。在使用高阶组件(HOC)或渲染属性(renderprops)时,如果不加以控制,很容易形成嵌套层级过深的组......
  • React的核心原理:组件化开发深度解析
    React的核心原理:组件化开发深度解析React,作为当今最流行的前端框架之一,其成功的背后离不开其核心原理——组件化开发。组件化开发不仅简化了前端开发的复杂性,还提高了代码的可重用性和可维护性。本文将深入探讨React组件化开发的原理、优势以及实践中的注意事项。一、组件化开发......
  • vue3:如何进行组件间的信息传递
    这里以父组件——主页面|子组件1——对话框|子组件2——按钮为例父组件——主页面import{provide,ref}from"vue";#创建对象,并且其有一个value属性,现在定义为falseconstdialogVisible=ref(false);constsetdialogVisible=(value)=>{dialogVisible.value=value;......
  • 界面组件DevExpress WinForms v23.2 - 数据展示、UI模板功能全新升级
    DevExpressWinForms拥有180+组件和UI库,能为WindowsForms平台创建具有影响力的业务解决方案。DevExpressWinForms能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜任!DevExpressWinForms控件日前正式发布了v23.2,此版......
  • POI2005KOS-Dicing
    网络流#二分#POI#Year2005考虑二分答案,用\(Dinic\)来\(check\)具体来说,就是对每一个人限制流量,然后检查能不能把所有场全部流满#include<bits/stdc++.h>usingnamespacestd;#defineintlonglong#defineullunsignedlonglong#defineALL(a)(a).begin(),(a).......