vue3组件通信方式
使用props通信
父组件向子组件通信
父组件
<script setup lang="ts">
import ChildProps from "@/view/props/ChildProps.vue";
import { ref } from "vue";
let age = ref(18);
</script>
<template>
<div id="father">
<ChildProps name="Kang" :age="age"></ChildProps>
</div>
</template>
子组件
<script setup lang="ts">
let def = defineProps({
name: String,
age: Number,
});
console.log(def);
</script>
<template>
<div id="son">
<p>我的名字是{{ name }}</p>
<p>我的年纪是{{ age }}</p>
</div>
</template>
使用自定义事件
子组件向父组件传递
父组件
<template>
<div id="father">
<p>显示子组件传来的值{{ son }}</p>
<ChildrenEvent @child-event="message"></ChildrenEvent>
</div>
</template>
<script setup lang="ts">
import ChildrenEvent from "@/view/event/ChildrenEvent.vue";
import { ref } from "vue";
let son = ref("");
let message = (data1: any) => {
son = data1;
console.log(data1);
};
</script>
子组件
<template>
<div id="son">
<button @click="upInfo"></button>
</div>
</template>
<script setup lang="ts">
const $emit = defineEmits(["child-event"]);
function upInfo(): any {
const message = "这是子组件传来的";
$emit("child-event", message);
}
</script>
<style scoped>
button {
width: 200px;
height: 200px;
background-color: black;
}
</style>
全局事件总线
兄弟组件相互通信
引入
$ npm install --save mitt
创建实例
import mitt from "mitt";
const $bus = mitt();
export default $bus;
兄弟组件
- 组件1
<template> <div id="son1"> <button @click="open">送你一个礼物</button> </div> </template> <script setup lang="ts"> import $bus from "@/view/eventbus/Bus"; // 发布sup事件 const open = () => { $bus.emit("sup", { code: true }); console.log("大儿子送一个礼物给小儿子"); }; </script> <style scoped> #son1 { width: 300px; height: 300px; background-color: yellow; } button { margin-top: 100px; line-height: 30px; width: 100px; height: 100px; background-color: white; } </style>
- 组件2
<template> <div id="son2"> <p v-show="flag"> <button>谢谢兄弟的礼物</button> </p> </div> </template> <script setup lang="ts"> import $bus from "@/view/eventbus/Bus"; import { ref } from "vue"; // 订阅sup事件,渲染button let flag = ref(false); $bus.on("sup", (payload: any) => { flag.value = payload.code; }); </script> <style scoped> #son2 { width: 300px; height: 300px; background-color: red; } button { margin-top: 100px; line-height: 30px; width: 100px; height: 100px; background-color: white; } </style>
父组件
<template>
<div id="father">
<ChildBus1></ChildBus1>
<ChildBus2></ChildBus2>
</div>
</template>
<script setup lang="ts">
import ChildBus1 from "@/view/eventbus/ChildBus1.vue";
import ChildBus2 from "@/view/eventbus/ChildBus2.vue";
</script>
<style scoped>
#father {
display: flex;
justify-content: space-between;
}
</style>
v-model 父子间通信
<!--使用v-model对于组件而言等价于-->
<Demo v-model='text'></Demo>
<Demo :modelValue="text" @update:modelValue="a"></Demo>
- Props:v-model 中的值作为一个名为
value
的 props传递给组件。这个 props 的值来自父组件中 v-model 所绑定的数据。- Event:Vue 监听组件内部的输入事件(通常是 input 或者 change 事件),并通过 emit 发出一个名为
update:modelValue
的事件,将新的值作为参数传递给父组件。这样就实现了在组件内部实现数据的双向绑定。当父组件的数据变化时,通过
value
props 将新的值传递给子组件,子组件更新自身的状态并展示新的值。当子组件的值发生变化时,通过update:modelValue
事件将新的值传递给父组件,父组件接收到值后更新自身的数据。
子组件
<template>
<div class="son">
<p>这里是儿子的金额{{ modelMoney }}</p>
<button @click="change"></button>
</div>
</template>
<script setup lang="ts">
let props = defineProps(["modelMoney"]);
const $emit = defineEmits(["update:modelMoney"]);
let change = () => {
$emit("update:modelMoney", props.modelMoney + 1);
};
</script>
<style scoped></style>
父组件
<template>
<div class="father">
<p>这里是父亲的金额{{ money }}</p>
<!-- <ChildModel-->
<!-- :modelMoney="money"-->
<!-- @update:modelMoney="updateMoney"-->
<!-- ></ChildModel>-->
<ChildModel v-model:modelMoney="money"></ChildModel>
</div>
</template>
<script setup lang="ts">
import ChildModel from "@/view/model/ChildModel.vue";
import { ref } from "vue";
let money = ref(100);
let updateMoney = (data: number) => {
money.value = data;
};
</script>
<style scoped></style>
Attributes通信
Attributes和props的传递类似,但是权利没有props大,当使用defineProps获取到属性之后,Attributes将获取不到。其次是Attributes不仅可以获取属性,还可以拿到事件
子组件
<template>
<div class="son">父亲给的值{{ $attrs.id }}---{{ $attrs.type }}</div>
</template>
<script setup lang="ts">
import { useAttrs } from "vue";
let $attrs = useAttrs();
console.log("attrs的值有:", $attrs);
// 如果一起使用,useAttrs将会拿不到父组件传来的数据
// let props = defineProps({
// id: String,
// type: String,
// });
</script>
<style scoped>
.son {
width: 300px;
height: 300px;
background-color: red;
}
</style>
父组件
<template>
<div id="parent">
<ChildAttrs id="1" type="big" @click="h"></ChildAttrs>
</div>
</template>
<script setup lang="ts">
import ChildAttrs from "@/view/attrs/ChildAttrs.vue";
const h = () => {
alert("你好");
};
</script>
<style scoped></style>
使用ref和$parent获取实例通信
通过ref获取子组件的实例,$parent获取父组件的对象
儿子组件
<template>
<div class="son">
<p>儿子有{{ money }}钱</p>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
let money = ref(199999);
// 暴露属性
defineExpose({
money,
});
</script>
<style scoped>
.son {
width: 200px;
height: 200px;
background-color: blue;
}
</style>
女儿组件
<template>
<div class="dau">
<button @click="change($parent)">问父亲要钱</button>
<p>女儿有{{ money }}钱</p>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
let money = ref(10000);
let change = ($parent: any) => {
console.log("父亲的实例对象:", $parent);
$parent.money -= 10000;
money.value += 10000;
};
</script>
<style scoped>
.dau {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
父组件
<template>
<div class="father">
<p>父亲有{{ money }}钱</p>
<button @click="change">问儿子要钱</button>
<ChildRef ref="son"></ChildRef>
<hr />
<ChildParent></ChildParent>
</div>
</template>
<script setup lang="ts">
import ChildParent from "@/view/refAndParent/ChildParent.vue";
import ChildRef from "@/view/refAndParent/ChildRef.vue";
import { ref } from "vue";
let money = ref(10000000);
// 儿子组件的实例
let son = ref();
let change = () => {
money.value += 1000;
console.log("儿子的实例对象", son);
son.value.money -= 1000;
};
//暴露属性
defineExpose({
money,
});
</script>
<style scoped>
.father {
width: 500px;
height: 500px;
background-color: red;
}
</style>
使用Provider和Inject通信
当有隔代组件的时候,使用Provider提供,然后使用Inject注入。当通过Inject改变从Provider获取的值时,两者的数据都会改变,因为是同一个对象
后代
<template>
<div class="son">
<p>后代有一辆{{ car }}</p>
<button @click="change">换车</button>
</div>
</template>
<script setup lang="ts">
import { inject } from "vue";
let car: any = inject("car");
// 改变值,父代也会跟着改变
let change = () => {
car.value = "法拉利";
};
</script>
<style scoped></style>
父代
<template>
<div class="father">
<p>祖先有一辆{{ car }}</p>
<ChildInject></ChildInject>
</div>
</template>
<script setup lang="ts">
import { provide, ref } from "vue";
import ChildInject from "@/view/InjectAndProvider/ChildInject.vue";
let car = ref("自行车");
provide("car", car);
</script>
<style scoped></style>
使用Pinia
- 导入
yarn add pinia
- 在main.ts中使用
import store from "@/store"; createApp(App).use(store).mount("#app");
- 定义store
import { defineStore } from "pinia";
const program = defineStore("program", {
state: () => {
return {
count: 1,
stu: {
id: 1,
name: "kang",
age: 18,
},
};
},
actions: {
changeStore(): void {
this.count++;
},
update(): void {
this.count++;
},
needVal(): any {
return this.stu;
},
},
getters: {},
});
export default program;
子组件
<template>
<div id="program">
<!-- <p>子组件的值为{{ refs.count.value }}</p>-->
<p>没有使用store的值{{ pro.count }}</p>
<p>解构自己想要的值{{ stu.id }}---{{ stu.age }}---{{ stu.name }}</p>
<button @click="update">自己的按钮</button>
</div>
</template>
<script setup lang="ts">
import program from "@/store/moudel/program";
import { storeToRefs } from "pinia";
const pro = program();
let { count, stu } = storeToRefs(pro);
// console.log("refs的值为:", refs);
const update = () => {
stu.value.id += 1;
stu.value.name += 1;
stu.value.age += 1;
};
</script>
<style scoped>
#program {
background-color: skyblue;
width: 200px;
height: 200px;
margin-left: 150px;
}
</style>
父组件
<template>
<div id="father">
<button @click="changeStore">通过pinia改变数据</button>
<button @click="slef">通过自己改变</button>
<ProgramPinia></ProgramPinia>
</div>
</template>
<script setup lang="ts">
import ProgramPinia from "@/view/pinia/ProgramPinia.vue";
import program from "@/store/moudel/program";
const pro = program();
let changeStore = () => {
pro.changeStore();
};
let slef = () => {
pro.count++;
};
</script>
<style scoped>
#father {
width: 500px;
height: 500px;
background-color: red;
}
</style>
使用slot(插槽)
子组件
<template>
<div class="son">
<!--默认插槽-->
<slot></slot>
<!--具名插槽-->
<slot name="A"></slot>
<slot name="B"></slot>
<!--作用域插槽-->
<slot title="泰酷啦"></slot>
<!--具名作用域插槽-->
<slot age="1998" name="twoHalfYears"></slot>
</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
父组件
<template>
<div id="father">
<!--默认插槽-->
<ChildSlot>hhh</ChildSlot>
<!--具名插槽-->
<ChildSlot>
<template #A>是非得失</template>
</ChildSlot>
<ChildSlot>
<template #B>无所谓</template>
</ChildSlot>
<!--作用域插槽-->
<ChildSlot>
<template v-slot="slotProps"> 这真是{{ slotProps.title }}</template>
</ChildSlot>
<!--作用域具名插槽-->
<ChildSlot>
<template #twoHalfYears="pro"> 出生于{{ pro.age }}</template>
</ChildSlot>
</div>
</template>
<script setup lang="ts">
import ChildSlot from "@/view/slot/ChildSlot.vue";
</script>
<style scoped></style>