vue3组件间通信
Posted wj-goodgoodstudy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue3组件间通信相关的知识,希望对你有一定的参考价值。
props
- 父子组件之间通信最好的方式
// 父组件
<template>
<div class="box">
<h1>props:这里是父组件</h1>
<hr />
<Child :money="money"></Child>
</div>
</template>
<script setup lang="ts">
//props:可以实现父子组件通信,props数据还是只读的!!!
import Child from "./Child.vue";
import ref from "vue";
let money = ref(10000);
</script>
// 子组件
<template>
<div class="son">
<h1>我是子组件</h1>
<p>props.money</p>
<!--props可以省略前面的名字--->
<p>money</p>
<button @click="updateProps">修改props数据</button>
</div>
</template>
<script setup lang="ts">
//需要使用到defineProps方法去接受父组件传递过来的数据
//defineProps是Vue3提供方法,不需要引入直接使用
let props = defineProps([\'money\']); //数组|对象写法都可以
//按钮点击的回调
const updateProps = ()=>
// props.money+=10; props:只读的
console.log(props)
</script>
自定义事件
- 父子组件通信
// 父组件
<template>
<div>
<button @click="handler1(1,2,3,$event)">点击我传递多个参数</button>
<hr>
<!--
vue2框架当中:这种写法自定义事件,可以通过.native修饰符变为原生DOM事件
vue3框架下面写法其实即为原生DOM事件
vue3:原生的DOM事件不管是放在标签身上、组件标签身上都是原生DOM事件
-->
<Event1 @click="handler2"></Event1>
<hr>
<!-- 绑定自定义事件xxx:实现子组件给父组件传递数据 -->
<Event2 @xxx="handler3" @click="handler4"></Event2>
</div>
</template>
<script setup lang="ts">
//引入子组件
import Event1 from \'./Event1.vue\';
//引入子组件
import Event2 from \'./Event2.vue\';
//事件回调--1
const handler = (event)=>
//event即为事件对象
console.log(event);
//事件回调--2
const handler1 = (a,b,c,$event)=>
console.log(a,b,c,$event)
//事件回调---3
const handler2 = ()=>
console.log(123);
//事件回调---4
const handler3 = (param1,param2)=>
console.log(param1,param2);
</script>
// 子组件2
<template>
<div class="child">
<p>我是子组件2</p>
<button @click="handler">点击我触发自定义事件xxx</button>
<button @click="$emit(\'click\',\'111\',\'222\')">点击我触发自定义事件click</button>
</div>
</template>
<script setup lang="ts">
//利用defineEmits方法返回函数触发自定义事件
//defineEmits方法不需要引入直接使用
let $emit = defineEmits([\'xxx\',\'click\']);
//按钮点击回调
const handler = () =>
//第一个参数:事件类型 第二个|三个|N参数即为注入数据
$emit(\'xxx\',\'333\',\'444\');
;
</script>
全局事件总线
- 任意组件间通信
// 使用mitt,先封装一个$bus
// bus
//引入mitt插件:mitt一个方法,方法执行会返回bus对象
import mitt from \'mitt\';
const $bus = mitt();
export default $bus;
// 组件1
<template>
<div class="child1">
<h3>我是子组件child1</h3>
</div>
</template>
<script setup lang="ts">
import $bus from "../../bus";
//组合式API函数
import onMounted from "vue";
//组件挂载完毕的时候,当前组件绑定一个事件,接受将来兄弟组件传递的数据
onMounted(() =>
//第一个参数:即为事件类型 第二个参数:即为事件回调
$bus.on("child1", (str) =>
console.log(str);
);
);
</script>
// 组件2
<template>
<div class="child2">
<h2>我是子组件child2</h2>
<button @click="handler">点击给child1传值</button>
</div>
</template>
<script setup lang="ts">
//引入$bus对象
import $bus from \'../../bus\';
//点击按钮回调
const handler = ()=>
$bus.emit(\'child1\',"你好");
</script>
v-model
- 本质上还是使用了自定义事件
// v-model原理
// 在原生元素上
<input v-model="text" />
// 等价展开
<input
:value="text"
@input="text = $event.target.value"
/>
// 在组件上展开
<Child1
:modelValue="text"
@update:modelValue="newValue => text = newValue"
/>
// Child1
<template>
<input
:value="modelValue"
@input="$emit(\'update:modelValue\', $event.target.value)"
/>
</template>
<script setup>
// 这里只能使用modleValue和upate:modleValue
defineProps([\'modelValue\'])
defineEmits([\'update:modelValue\'])
</script>
// 指定参数名称
<Child2 v-model:text="text" />
// Child2
<script setup>
defineProps([\'text\'])
defineEmits([\'update:text\'])
</script>
// 实际上指定参数名称就是在子组件的defineEmits([\'update:指定参数\'])、defineProps([指定参数])
useAttrs
- 接收父组件的属性和方法,多用于组件二次封装
// 父组件
<template>
<div>
<h1>useAttrs</h1>
<!-- 自定义组件 -->
<HintButton type="primary" size="small" :icon="Edit" title="编辑按钮" @click="handler" ></HintButton>
</div>
</template>
// 子组件
<template>
<div :title="title">
<el-button :="$attrs"></el-button>
</div>
</template>
<script setup lang="ts">
//引入useAttrs方法:获取组件标签身上属性与事件
import useAttrs from "vue";
//此方法执行会返回一个对象,里面是传过来的一堆属性与方法
let $attrs = useAttrs();
//万一用props接受title
let props = defineProps(["title"]);
//props与useAttrs方法都可以获取父组件传递过来的属性与属性值
//但是props接受了useAttrs方法就获取不到了
console.log($attrs);
</script>
ref和$parent
- ref 可以获取真实的DOM节点,可以获取到子组件实例VC
- $parent 可以在子组件内部获取到父组件的实例
- 只能获取通过 defineExpose 暴露出来的对象
// 父组件
<template>
<div class="box">
<h1>我是父亲曹操:money</h1>
<button @click="handler">找我的儿子曹植借10元</button>
<hr>
<Son ref="son"></Son>
<hr>
<Dau></Dau>
</div>
</template>
<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>
// 子组件1
<template>
<div class="son">
<h3>我是子组件:曹植money</h3>
</div>
</template>
<script setup lang="ts">
import ref from \'vue\';
//儿子钱数
let money = ref(666);
const fly = ()=>
console.log(\'我可以飞\');
//组件内部数据对外关闭的,别人不能访问
//如果想让外部访问需要通过defineExpose方法对外暴露
defineExpose(
money,
fly
)
</script>
// 子组件2
<template>
<div class="dau">
<h1>我是闺女曹杰money</h1>
<button @click="handler($parent)">点击我爸爸给我10000元</button>
</div>
</template>
<script setup lang="ts">
import ref from \'vue\';
//闺女钱数
let money = ref(999999);
//闺女按钮点击回调
const handler = ($parent)=>
money.value+=10000;
$parent.money-=10000;
</script>
provide 和 inject
- 与后代组件通信,provide 注入数据,inject 接收数据
// 祖先组件
<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>
// 孙子组件
<script setup lang="ts">
import inject from \'vue\';
//注入祖先组件提供数据
//需要参数:即为祖先提供数据的key
let car = inject(\'TOKEN\');
const updateCar = ()=>
car.value = \'自行车\';
</script>
pinia
- vuex 升级版,任意组件间通信
pinia 只有三个核心概念
- state,存放数据和状态
- actions,存放数据和状态处理的方法
- getters,类似于计算属性
store/index.ts
// 创建 pinia 仓库,在 src 根目录下创建 store 文件夹,在 store 中创建 modules 文件夹存放小仓库的数据,在 store 中创建 index.ts 文件进行大仓库的创建
// index.ts
//创建大仓库
import createPinia from \'pinia\';
//createPinia方法可以用于创建大仓库
let store = createPinia();
//对外暴露,安装仓库
export default store;
store/modules/info.ts
//定义info小仓库
import defineStore from "pinia";
//第一个仓库:小仓库名字 第二个参数:小仓库配置对象
//defineStore方法执行会返回一个函数,函数作用就是让组件可以获取到仓库数据
let useInfoStore = defineStore("info",
//存储数据:state,使用箭头函数声明会自动推断属性的类型
state: () =>
return
count: 99,
arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
,
actions:
//注意:函数没有context上下文对象
//没有commit、没有mutations去修改数据
updateNum(a: number, b: number)
this.count += a;
,
getters:
// 数组求和
total()
let result:any = this.arr.reduce((prev: number, next: number) =>
return prev + next;
, 0);
return result;
);
//对外暴露方法
export default useInfoStore;
store/modules/todo.ts
//定义组合式API仓库,有点类似于自定义 hook 的感觉了
import defineStore from "pinia";
import ref, computed,watch from \'vue\';
//创建小仓库
let useTodoStore = defineStore(\'todo\', () =>
// 声明响应式变量
let todos = ref([ id: 1, title: \'吃饭\' , id: 2, title: \'睡觉\' , id: 3, title: \'打豆豆\' ]);
let arr = ref([1,2,3,4,5]);
// 使用计算属性
const total = computed(() =>
return arr.value.reduce((prev, next) =>
return prev + next;
, 0)
)
//务必要返回一个对象:属性与方法可以提供给组件使用
return
todos,
arr,
total,
updateTodo()
todos.value.push( id: 4, title: \'组合式API方法\' );
);
export default useTodoStore;
组件1
<template>
<div class="child">
// 函数.属性获取store的属性值
<h1> infoStore.count ---infoStore.total</h1>
<button @click="updateCount">点击我修改仓库数据</button>
</div>
</template>
<script setup lang="ts">
import useInfoStore from "../../store/modules/info";
//获取小仓库对象,defineStore 返回的是一个函数,所以需要调用函数的方式来调用 store
let infoStore = useInfoStore();
console.log(infoStore);
//修改数据方法
const updateCount = () =>
//仓库调用自身的方法去修改仓库的数据,函数.方法调用方法
infoStore.updateNum(66,77);
;
</script>
插槽
- 也是父子组件通信的方式
// 组件1
<Test>
<div>
<pre>默认</pre>
</div>
<!-- 具名插槽填充a -->
<template #a>
<div>我是填充具名插槽a位置结构</div>
</template>
<!-- 具名插槽填充b v-slot指令可以简化为# -->
<template #b>
<div>我是填充具名插槽b位置结构</div>
</template>
</Test>
// 默认插槽和具名插槽
<template>
<div class="box">
<h1>我是子组件默认插槽</h1>
<!-- 默认插槽 -->
<slot></slot>
<h1>我是子组件默认插槽</h1>
<h1>具名插槽填充数据</h1>
<slot name="a"></slot>
<h1>具名插槽填充数据</h1>
<h1>具名插槽填充数据</h1>
<slot name="b"></slot>
<h1>具名插槽填充数据</h1>
</div>
</template>
// 组件2
// 根据子组件传过来的值 done 来决定展示的颜色
<Test1 :todos="todos">
<template v-slot=" $row, $index ">
<p :>
$row.title -- $index
</p>
</template>
</Test1>
// 作用域插槽
<template>
<div class="box">
<h1>作用域插槽</h1>
<ul>
<li v-for="(item, index) in todos" :key="item.id">
<!--作用域插槽:可以讲数据回传给父组件-->
<slot :$row="item" :$index="index"></slot>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
//通过props接受父组件传递数据
defineProps(["todos"]);
</script>
Vue 组件间的通信
Vue 组件间的通信
组件间通信方式
- props :父组件向子组件传递数据。
- $emit :自定义事件。
- slot :插槽分发内容,带上标签数据一起传输,主要是父组件想子组件传输。
组件间通信规则
- 不要在子组件中直接修改父组件传递的数据。
- 数据初始化的时候,应当看初始化的数据是否用于多个组件中,如果需要被用于多个组件中,则初始化在父组件中;如果只在一个组件中使用,那就初始化在这个要使用的组件中。
- 数据初始化在哪个组件,更新数据的方法(函数)就应该定义在哪个组件中。
以上是关于vue3组件间通信的主要内容,如果未能解决你的问题,请参考以下文章
「 VUE3 + TS + Vite 」父子组件间如何通信?