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. 组件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>

  1. 组件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>
  1. Props:v-model 中的值作为一个名为 value 的 props传递给组件。这个 props 的值来自父组件中 v-model 所绑定的数据。
  2. 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

  1. 导入

yarn add pinia

  1. 在main.ts中使用

import store from "@/store"; createApp(App).use(store).mount("#app");

  1. 定义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>

posted @ 2023-05-20 00:09  Kang_kin  阅读(59)  评论(0编辑  收藏  举报