vue3中组件通信的方法

Vue 3 提供了多种组件通信的方法,这里罗列了九种种方法;

Props(属性)

  • 父组件可以通过属性传递数据给子组件。
  • 子组件通过 props 接收父组件传递的数据。
<!-- 父组件 -->
<template>
  <ChildComponent :message="parentMessage" />
</template>

<script setup>
import ChildComponent from './ChildComponent.vue';
import { ref } from "vue";
let parentMessage = ref('Hello from parent!');
</script>

<!-- 子组件 -->
<template>
  <!-- 可以使用 props.message 或者省略peops直接用message -->
  <div>{{ props.message }}</div>
  <div>{{ message }}</div>
</template>

<script setup>
  //确实不需要显式引入 defineProps
  //因为 Vue 3 在 script setup 中会自动引入和注入 defineProps
let props = defineProps(['message']);//参数可以是对象或者数组,且数据是只读的,!!!不可以直接改

</script>

自定义事件(Custom Events)

  • 子组件可以通过 $emit 触发自定义事件。
  • 父组件通过 v-on 监听这些事件。
<!-- 子组件 -->
<template>
  <button @click="sendMessage">Send Message</button>
</template>

<script setup>
//利用defineEmits方法返回函数触发自定义事件
//defineEmits方法不需要引入直接使用
//如果参数变成['message-sent','click'],那么click原生事件变成自定义事件,可供子组件调用
let messageEmits = defineEmits(['message-sent']);
const sendMessage=()=>{
  //第一个参数为事件类型 第二 ~ N参数为注入传参数据
  messageEmits('message-sent','first message','second message')
}
</script>

<!-- 父组件 -->
<template>
  <ChildComponent @message-sent="handleMessage" @click="handlerClick"/>
</template>

<script setup>
import ChildComponent from './ChildComponent.vue';

//自定义事件回调
const handleMessage = (data1,data2)=>{
    //事件传参
    console.log(data1,data2);//打印结果为 'first message','second message'
    
}
//这里是原生的click事件
const handlerClick = (event)=>{
  //event即为事件对象
  console.log(event);
}
</script>

mitt 全局事件总线:

mitt 是一个小巧且强大的事件总线库,可以用于更灵活的组件通信。使用 mitt,你可以在任何组件之间进行事件的发布和订阅。

  1. 安装 mitt:
npm install mitt
  1. 在组件中使用 mitt:

独立声明,方便按需引入


<!-- /src/emitter/index.js -->
import mitt from 'mitt';
const emitter = mitt();
export default emitter;
<!-- 父组件 -->
  <template>
    <div>
      <button @click="sendMessage">Send Message</button>
    </div>
  </template>

  <script setup>
    import emitter from "@/emitter";
    const sendMessage = ()=>{
      //发布消息触发事件使用emit方法,第一个参数是自定义事件类型,后面是传输的消息
      emitter.emit('message-sent', 'Hello from parent!');
    }
  </script>
<!-- 子组件 -->
  <template>
    <div>
      <h3>{{ message }}</h3>
    </div>
  </template>

  <script setup>
import emitter from "@/emitter";
    //组合式API函数
import { onMounted,ref } from "vue";
//第一个参数:即为自定义事件类型  第二个参数:即为事件回调
let message= ref('')
onMounted(() => {
  emitter.on("message-sent", (mes) => {
    console.log(mes);
    message.value = mes
  });
});
  </script>

使用 mitt 可以更轻松地进行多组件之间的通信,而不需要通过父子组件关系或者 provide/inject 来传递数据。mitt 的使用方式类似于浏览器的事件系统,更具灵活性。

组件v-model 通信

v-model 是 Vue 3 中用于实现父子组件之间双向绑定的一种机制。通过使用 v-model,可以在父组件中使用 v-model 指令将数据双向绑定到子组件的 prop 和事件上,使得父子组件之间的通信更加方便。
Vue 3 默认期望子组件接收一个名为 modelValue 的 prop,并且通过 update:modelValue 事件来实现双向绑定。不过,你确实可以自定义这个名字。
这里就直接演示自定义的prop,可能这块有一些不还好理解,因为 **v-model 组件通信 **里层帮我们做了很多事,让我们省去了很多重复性的代码,它对于封装组件非常有效且干净,以下我用案例来解析一下;

<!-- ParentComponent.vue -->
<template>
  <div>
    <!-- customProp为我自定义的名字
    这里的v-model做了以下事
    		第一:给子组件传递 props[customProp] = 'Hello from parent!'
       第二:给子组件绑定自定义事件update:customProp
   -->
    <ChildComponent v-model:customProp="parentMessage" />
    <p>Message in parent: {{ parentMessage }}</p>
  </div>
</template>

<script setup>
  import ChildComponent from './ChildComponent.vue';
  import { ref } from 'vue';

  const parentMessage = ref('Hello from parent!');
</script>
<!-- ChildComponent.vue -->
  <template>
    <div>
      <input :value="customProp" @input="updateCustomProp" />
      <p>Message in child: {{ customProp }}</p>
    </div>
  </template>

  <script setup>
    import { ref, defineProps, defineEmits } from 'vue';

    const props = defineProps(['customProp']);
    const { emit } = defineEmits(['update:customProp']);

    const updateCustomProp = (event) => {
      emit('update:customProp', event.target.value);
    };
  </script>

我将 v-model 的 prop 名称设为了 customProp。父组件通过 v-model:customProp=“parentMessage”parentMessage 数据绑定到子组件上,子组件内部使用 :value@input 来绑定输入框的值和触发更新事件。这样,当子组件的输入框变化时,会通过 update:customProp 事件通知父组件更新数据。

Provide / Inject(提供/注入)

父组件通过 provide 提供数据,子孙组件通过 inject 注入数据,可以实现隔辈组件传递数据,这里用三个组件案例来说明;

<!-- 父组件 -->
<template>
  <div>
    <p>Message in parent: {{ message }}</p>
    <ChildComponent />
  </div>
</template>

<script setup>
  import { provide, ref } from 'vue';

  const message = ref('Hello from parent!');

  // 使用 provide 提供数据
  provide('message', message);
</script>

<!-- 子组件 -->
<template>
  <div>
    <p>Message in child: {{ childMessage }}</p>
    <GrandchildComponent />
  </div>
</template>

<script setup>
  import { inject, ref } from 'vue';
  import GrandchildComponent from './GrandchildComponent.vue';

  // 使用 inject 接收提供的数据
  const childMessage = inject('message');
</script>


<!-- 孙组件 -->
<template>
  <div>
    <p>Message in grandchild: {{ grandchildMessage }}</p>
    <button @click="updateMessage">孙子修改数据</button>
  </div>
</template>

<script setup>
  import { inject } from 'vue';

  // 使用 inject 接收提供的数据
  const grandchildMessage = inject('message');
  //可以直接更新父组件
  const updateMessage = ()=>{
     grandchildMessage.value  = 'Default message from grandchild';
  }
</script>

在这个例子中,ParentComponent 组件通过 provide 提供了一个名为 ‘message’ 的数据,然后 ChildComponent 组件和 GrandchildComponent 组件分别通过 inject 接收了这个数据。这样就实现了隔代组件通信,父组件的数据可以被子组件和孙子组件访问到,同时子孙也可以直接修改数据同步到父组件;
请注意,使用 provideinject 时,要确保在子组件中使用 inject 接收提供的数据时,给定的 key(这里是 ‘message’)要与父组件中 provide 的 key 一致。

useAttrs

useAttrs 是 Vue 3 中的一个 Composition API 函数,它可以用于在组件中访问父组件传递的所有属性。通过 useAttrs,你可以获取父组件传递给当前组件的所有属性,并在需要的时候使用它们。

以下是一个简单的例子:
下是一个简单的示例,演示如何使用 useAttrs

<template>
    <div>
      <p>Message: {{ message }}</p>
      <p>Additional Attribute: {{ additionalAttribute }}</p>
    </div>
  </template>

  <script setup>
    import { useAttrs, ref } from 'vue';

    const { message, additionalAttribute } = useAttrs();
  </script>

在这个例子中,useAttrs 会返回一个包含组件上所有未声明的属性的对象。你可以通过解构赋值获取其中的属性值。这样,即使在组件上没有声明这些属性,你仍然可以访问它们。

例如,如果你使用这个组件时传递了一个未在组件中声明的属性:

<MyComponent message="Hello from parent" additionalAttribute="Additional Data" />

那么在组件内部,通过 useAttrs 就可以获取这两个属性的值。需要注意的是,如果属性在组件中声明了,它将不会出现在 useAttrs 的返回值中,比如这里同时使用使用了defineProps来接收参数message,那么message就不在useAttrs 中返回了

Ref(引用)和$prarent

  • 父组件可以通过 ref 获取子组件的引用。
<!-- 父组件 -->
<template>
  <child-component ref="childRef" />
  <h3>{{parsentMessage}}</h3>
  <button @click="getChildData">拿到儿子暴露的的属性和方法</button>
</template>

<script setup>
import {ref} from 'vue';
import ChildComponent from './ChildComponent.vue';
//获取子组件的实例
let childRef = ref();
const parsentMessage = ref('parent show message')
const getChildData = ()=>{//使用ref修改数据或者调用方法,前提是子组件存在该方法且暴露出来
  childRef.value.childMessage = 'updata child show message'
}

//对外暴露,只有对外暴露了ref或者$parent才能查询到
defineExpose({
   parsentMessage
})
</script>

<!-- 子组件 -->
<template>
  <!-- Child component template -->
  <div>
    <h3>{{childMessage}}</h3>
    <button @click="getparentData">拿到父亲暴露的属性和方法</button>
  </div>
</template>

<script setup>
import {ref} from 'vue';
const childMessage = ref('child show message')
const getparentData = ()=>{
  
}

//对外暴露,只有对外暴露了ref或者$parent才能查询到
defineExpose({
   childMessage
})
</script>

Pinia(状态管理库)

Pinia 是一个状态管理库,它可以在 Vue 3 项目中用于全局状态管理,而且也可以用于组件通信。虽然 Pinia 主要是为状态管理而设计的,但它的 store 实例也可以用于简单的组件通信。
以下是一个简单的例子,演示了如何使用 Pinia 在组件之间进行通信:

  1. 安装 Pinia:
npm install pinia
  1. 创建 Pinia Store:
// src/pinia/index.js
import { createPinia } from 'pinia';

const pinia = createPinia();

export default pinia;
  1. 创建 Pinia Store 实例:
// src/pinia/store.js
import { defineStore } from 'pinia';

export const usePinia = defineStore('pinia', {
  state: () => ({
    message: ''
  }),
  actions: {
    setMessage(message) {
      this.message = message;
    }
  }
});
  1. 在组件中使用 Pinia:
<!-- ParentComponent.vue -->
<template>
  <div>
    <button @click="sendMessage">Send Message</button>
  </div>
</template>

<script setup>
import {usePinia} from "@/pinia/store";
//获取Pinia对象
let infoStore = usePinia();
const sendMessage =()=> {
    // 在 Pinia store 中设置消息
    infoStore.setMessage('Hello from parent!');
  }
</script>
<!-- ChildComponent.vue -->
<template>
  <div>
    <p>Received message: {{ infoStore.message }}</p>
  </div>
</template>

<script setup>
import pinia from '@/pinia';
import {usePinia} from "@/pinia/store";
//获取Pinia对象
let infoStore = usePinia();
</script>
  1. 在 main.js 中挂载 Pinia:
// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import pinia from '@/pinia';
import { createPinia } from 'pinia';

const app = createApp(App);

app.use(pinia);
app.mount('#app');

上述例子中,我们使用 Pinia 创建了一个全局的 store,并在父组件中通过 store 设置消息,然后在子组件中通过 store 获取消息,实现了组件之间的通信。请注意,这只是 Pinia 在组件通信方面的一种用法,Pinia 主要是用于全局状态管理,如果你的应用有更复杂的状态管理需求,Pinia 提供了更丰富的功能。

插槽(Slots)父子通信

通过插槽(Slots)也是一种在 Vue 组件之间进行通信的方法。插槽允许父组件向子组件传递内容,子组件可以在指定的位置插入这些内容。这样,父组件和子组件之间可以进行更灵活的通信。

以下是一个简单的例子:

<!-- ParentComponent.vue -->
<template>
  <div>
    <childComponent>
      <!-- 通过插槽传递数据 -->
      <template v-slot:messageSlot="{ message }">
        <p>Received message: {{ message }}</p>
      </template>
    </childComponent>
  </div>
</template>

<script setup>
import childComponent from "./child-component.vue";
</script>
<!-- ChildComponent.vue -->
<template>
  <div>
    <!-- 使用插槽接收数据 -->
    <slot name="messageSlot" :message="message" />
  </div>
</template>

<script setup>
import { defineComponent, ref } from 'vue';
import {ref} from 'vue'
const message= ref('Hello from child!')
</script>

在上面的例子中,通过使用插槽,父组件向子组件传递了一个名为 messageSlot 的插槽,并在插槽内部渲染了一个包含消息的 <p> 元素。子组件则通过 <slot> 元素接收并渲染这个插槽,并将 message 属性传递给插槽。

通过这种方式,父组件和子组件可以更加自由地通信,父组件可以决定传递什么内容,而子组件可以在不同的插槽中接收和处理这些内容。这种方式尤其适用于需要更灵活组件结构的情况。

这些是一些常见的 Vue 3 组件通信的方法。选择合适的方法取决于你的具体需求和项目结构;
项目中大部分组件通信使用 props 和自定义事件就是可解决;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值