本文主要介绍Vue3组件的七种通讯方法,并采用组合式API写法。

涉及到的知识点如下:

  • props
  • emit
  • expose/ref
  • v-model
  • provide/inject
  • mitt
  • Vuex/pinia

一、props【父组件传值给子组件】

  import Child from "@/components/Child.vue";let message = "Vue3父传子";
  {{ meg }}const props = defineProps({  meg: {    type: String,    default: "",  },});console.log(props.meg); //在js中使用props.xxx 在html中可直接用xxx

二、emit【子组件通知父组件触发一个事件,且可以传值给父组件】

  父组件:{{ message }}    import { ref } from "vue";import Child from "@/components/Child.vue";let message = ref("Vue3父传子");// 更改 message 的值,data是从子组件传过来的function changeMessage(data) {  message.value = data;}
  子组件:// 注册一个自定义事件名,向上传递时告诉父组件要触发的事件。const emit = defineEmits(["changeMsg"]);function handleClick() {  // 参数1:事件名  // 参数2:传给父组件的值  emit("changeMsg", "子组件修改父组件的值");}

三、expose / ref【子组件可以通过expose暴露自身的方法和数据,父组件通过ref获取到子组件并调用其方法或访问数据。

  父组件:拿到子组件的message数据:{{ msg }}    
import { ref, onMounted } from "vue";import Child from "@/components/Child.vue";const childRef = ref(null); // 通过 模板ref 绑定子组件const msg = ref("");onMounted(() => { // 在加载完成后,将子组件的 message 赋值给 msg msg.value = childRef.value.message;});function childFun() { // 调用子组件的 changeMessage 方法 childRef.value.changeMessage("父组件调用子组件"); // 重新将 子组件的message 赋值给 msg msg.value = childRef.value.message;}
  子组件:{{ message }}import { ref } from "vue";const message = ref("子组件的值");function changeMessage(data) {  message.value = data;}//使用 defineExpose 向外暴露指定的数据和方法defineExpose({  message,  changeMessage,});

四、v-model【支持多个数据双向绑定

  import { ref } from "vue";import Child from "@/components/Child.vue";const message1 = ref("标题1");const message2 = ref("标题2");
   {{ msg1 }}   {{ msg2 }}import { ref } from "vue";// 接收const props = defineProps({  msg1: String,  msg2: String,});const emit = defineEmits(["update:msg1", "update:msg2"]);function changeMsg1() {  emit("update:msg1", "修改标题1");}function changeMsg2() {  emit("update:msg2", "修改标题2");}

五、provide / inject【多层传值】

**注:遇到多层传值时,使用props和emit的方式会显得比较笨拙。这时就可以是用provide和inject。

provide是在父组件里使用的,可以往下传值。inject是在子(后代)组件里使用的,可以往上取值。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。

  import { ref, provide, readonly } from "vue";import Child from "@/components/Child.vue";const msg1 = ref("");const msg2 = ref("标题1");// 使用readonly后子组件直接修改会发出警告,需要调用provide往下传的方法来修改provide("msg1", readonly(msg1));provide("msg2", msg2);provide("changeName", (value) => {  msg1.value = value;});
      msg1: {{ msg1 }}    msg2: {{ msg2 }}      import { inject } from "vue";const msg1 = inject("msg1", "默认值"); // 看看有没有值,没值的话就适用默认值。const msg2 = inject("msg2");const changeName = inject("changeName");function handleClick() {  // 这样写不合适,因为vue里推荐使用单向数据流,当父级使用readonly后,子元素直接修改会发出警告。  // msg1.value = '修改后的值'  // 正确的方式  changeName("修改后的标题1");  // 因为 msg2 没被 readonly 过,所以可以直接修改值  msg2.value = "标题2";}

六、mitt

Vue3中没有了EventBus跨组件通信,但是现在有了一个替代的方案mitt.js,原理还是EventBus。

先安装npm i mitt -S然后像以前封装 bus 一样,封装一下

//mitt.jsimport mitt from 'mitt'const mitt = mitt()export default mitt
// 组件 Aimport mitt from './mitt'const handleClick = () => {    mitt.emit('handleChange')}// 组件 Bimport mitt from './mitt'import { onUnmounted } from 'vue'const someMethed = () => { ... }mitt.on('handleChange',someMethed)onUnmounted(()=>{    mitt.off('handleChange',someMethed)})