VUE3中的组件通信七种方法介绍与基本使用

本文介绍了Vue.js中父组件向子组件的prop传递、子组件向父组件的emit、双向数据绑定v-model、vuex和pinia状态管理、事件总线以及依赖注入等组件间通信的方法。
摘要由CSDN通过智能技术生成

一、父传子(props)

在子组件中可以使用defineProps接收父组件向子组件的传值

父组件fatherPage.vue:

<template>
  <div class="father">
    <button @click="a = a + 1">按钮</button>
    <childPage :a="a" />
  </div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import childPage from "./childPage.vue";
const a = ref<number>(0);
</script>
<style scoped lang="less">
.father {
  width: 100%;
  height: 100vh;
  background-color: rgb(7, 14, 17);
  display: flex;
  justify-content: center;
  align-items: center;
  button {
    width: 100px;
    height: 60px;
    cursor: pointer;
  }
}
</style>

子组件childPage.vue:

<template>
  <div class="childPage">
    {{ a }}
  </div>
</template>
<script lang="ts" setup>
import { computed, defineProps } from "vue";
// 含默认值
// const porps = defineProps({
//   a: {
//     type: Number,
//     default: 0,
//   },
// });
const props = defineProps({ a: Number });
// 用computed使用props
const a = computed(() => props.a);
</script>
<style scoped lang="less">
.childPage {
  width: 100px;
  height: 60px;
  background-color: rgba(0, 0, 0, 0);
  display: flex;
  justify-content: center;
  align-items: center;
  color: #ffffff;
}
</style>

效果:

二、子传父(emits)

在子组件中可以使用defineEmits向父组件传递信息。

父组件fatherPage.vue:

<template>
  <div class="father">
    {{ a }}
    <childPage @update:a="update" />
  </div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import childPage from "./childPage.vue";
const a = ref<number>(0);
const update = (emitValue: number) => {
  a.value = emitValue;
};
</script>
<style scoped lang="less">
.father {
  width: 100%;
  height: 100vh;
  background-color: rgb(7, 14, 17);
  color: #ffffff;
  display: flex;
  justify-content: center;
  align-items: center;
  button {
    width: 100px;
    height: 60px;
    cursor: pointer;
  }
}
</style>

子组件childPage.vue:

1.通过点击触发emit传值

<template>
  <div class="childPage">
    <button @click="updateA">按钮</button>
  </div>
</template>

<script lang="ts" setup>
import { ref, defineEmits } from "vue";

const a = ref<number>(0);
const emit = defineEmits(["update:a"]);
const updateA = () => {
  a.value++;
  emit("update:a", a.value);
};
</script>

<style scoped lang="less">
.childPage {
  width: 100px;
  height: 60px;
  button {
    width: 100%;
    height: 100%;
    cursor: pointer;
  }
}
</style>

效果:

2.通过watch监听值的变化进行传值:

<template>
  <div class="childPage">
    <button @click="updateA">按钮</button>
  </div>
</template>

<script lang="ts" setup>
import { ref, defineEmits, watch } from "vue";

const a = ref<number>(0);
const emit = defineEmits(["update:a"]);
const updateA = () => {
  a.value++;
  setTimeout(() => {
    updateA();
  }, 1000);
};
watch(a, (newValue) => {
  emit("update:a", newValue);
});
</script>

<style scoped lang="less">
.childPage {
  width: 100px;
  height: 60px;
  button {
    width: 100%;
    height: 100%;
    cursor: pointer;
  }
}
</style>

效果:

三、父子组件进行双向绑定(v-model)

在子组件中可以使用defineModel与父组件进行双向绑定。注意:defineModel在vue3.3版本实验性使用,稳定版在vue3.4。

父组件:

<template>
  <div class="father">
    {{ a }}
    <childPage v-model="a" />
  </div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import childPage from "./childPage.vue";
const a = ref<number>();
</script>
<style scoped lang="less">
.father {
  width: 100%;
  height: 100vh;
  background-color: rgb(7, 14, 17);
  color: #ffffff;
  display: flex;
  justify-content: center;
  align-items: center;
  button {
    width: 100px;
    height: 60px;
    cursor: pointer;
  }
}
</style>

子组件:

<template>
  <div class="childPage">
    <button @click="updateA">按钮</button>
  </div>
</template>

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

const a = defineModel({
  type: Number,
  default: 0,
});

const updateA = () => {
  a.value += 1;
};
</script>

<style scoped lang="less">
.childPage {
  width: 100px;
  height: 60px;
  button {
    width: 100%;
    height: 100%;
    cursor: pointer;
  }
}
</style>

效果:

四、使用vuex进行组件通信

使用vuex可以进行组件之间的通信,父子组件与兄弟组件均可

在vuex文件index.ts中:

import { createStore } from "vuex";

export default createStore({
  state: {
    number: 0,
  },
  getters: {},
  mutations: {
    changeNumber(state, payload) {
      state.number = payload;
    },
  },
  actions: {},
  modules: {},
});

在第一个组件HomeView中:

<template>
  <div class="father">
    {{ a }}
    <AboutView />
  </div>
</template>

<script lang="ts" setup>
import { computed } from "vue";
import { useStore } from "vuex";
import AboutView from "./AboutView.vue";
const store = useStore();
const a = computed(() => store.state.number);
</script>
<style scoped lang="scss">
.father {
  width: 100%;
  height: 100vh;
  background-color: rgb(7, 14, 17);
  color: #ffffff;
  display: flex;
  justify-content: center;
  align-items: center;
}
</style>

在第二个组件AboutView中:

<template>
  <div class="childPage">
    <button @click="updateA">按钮</button>
  </div>
</template>

<script lang="ts" setup>
import { useStore } from "vuex";

const store = useStore();
let a = 0;

const updateA = () => {
  a++;
  store.commit("changeNumber", a);
};
</script>

<style scoped lang="scss">
.childPage {
  width: 100px;
  height: 60px;
  button {
    width: 100%;
    height: 100%;
    cursor: pointer;
  }
}
</style>

效果:

五、使用pinia进行组件通信

pinia有着与vuex类似的功能,同样可以实现组件间的通信。

在store的index.ts文件中:

import { defineStore } from "pinia";
import { ref } from "vue";
export const useCounterStore = defineStore("counter", () => {
  const count = ref(0);
  const changeCount = () => {
    count.value++;
  };

  return { count, changeCount };
});

在第一个组件fatherPage中:

<template>
  <div class="father">
    {{ count }}
    <childPage />
  </div>
</template>

<script lang="ts" setup>
import childPage from "./childPage.vue";
import { useCounterStore } from "../store/index";
import { storeToRefs } from "pinia";
const store = useCounterStore();
const { count } = storeToRefs(store);
</script>
<style scoped lang="less">
.father {
  width: 100%;
  height: 100vh;
  background-color: rgb(7, 14, 17);
  color: #ffffff;
  display: flex;
  justify-content: center;
  align-items: center;
  button {
    width: 100px;
    height: 60px;
    cursor: pointer;
  }
}
</style>

在第二个组件childPage中:

<template>
  <div class="childPage">
    <button @click="changeCount">按钮</button>
  </div>
</template>

<script lang="ts" setup>
import { useCounterStore } from "../store/index";
const store = useCounterStore();
const { changeCount } = store;
</script>

<style scoped lang="less">
.childPage {
  width: 100px;
  height: 60px;
  button {
    width: 100%;
    height: 100%;
    cursor: pointer;
  }
}
</style>

效果:

六、使用事件总线进行组件通信

我们可以利用第三方库比如说mitt以事件总线的方式实现传值

在mitt的index.ts中:

import mitt from "mitt";
const emitter = mitt();
export default emitter;

在第一个组件fatherPage中:

<template>
  <div class="father">
    {{ count }}
    <childPage />
  </div>
</template>

<script setup>
import childPage from "./childPage.vue";
import emitter from "@/mitt/index";
import { ref } from "vue";
const count = ref(0);
emitter.on("changeCount", (counter) => {
  count.value = counter;
});
</script>
<style scoped lang="less">
.father {
  width: 100%;
  height: 100vh;
  background-color: rgb(7, 14, 17);
  color: #ffffff;
  display: flex;
  justify-content: center;
  align-items: center;
  button {
    width: 100px;
    height: 60px;
    cursor: pointer;
  }
}
</style>

在第二个组件childPage中:

<template>
  <div class="childPage">
    <button @click="changeCount">按钮</button>
  </div>
</template>

<script setup>
import emitter from "../mitt/index";
let a = 0;
const changeCount = () => {
  a++;
  emitter.emit("changeCount", a);
};
</script>

<style scoped lang="less">
.childPage {
  width: 100px;
  height: 60px;
  button {
    width: 100%;
    height: 100%;
    cursor: pointer;
  }
}
</style>

效果:

七、依赖注入

使用provide与inject可以实现祖孙组件之间的传值,避免组件过深导致的props传值困难同时又没必要使用状态管理。

爷组件:

<template>
  <div class="page" @click="count++">
    <TwoPage />
  </div>
</template>
<script setup lang="ts">
import TwoPage from "./twoPage.vue";
import { ref, provide } from "vue";
const count = ref(0);
provide("count", count);
</script>
<style>
.page {
  width: 100vw;
  height: 100vh;
  background-color: rgb(8, 8, 8);
  display: flex;
  justify-content: center;
  align-items: center;
  color: seashell;
}
</style>

父组件:

<template>
  <div class="page">
    <threePage />
  </div>
</template>
<script setup lang="ts">
import threePage from "./threePage.vue";
</script>
<style></style>

子组件:

<template>
  <div class="page">{{ count }}</div>
</template>
<script setup lang="ts">
import { inject } from "vue";
const count = inject("count");
</script>

效果展示

八、更详细使用方式请参考以下文档

1,Vue.js - 渐进式 JavaScript 框架 | Vue.js

2,开始 | Vuex

3,Pinia | Pinia 中文手册 - 官网同步更新 v2.0.28

4,GitHub - developit/mitt: 🥊 Tiny 200 byte functional event emitter / pubsub.

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值