Vue3组件通信的7种方法,值得收藏!

组件通信是指组件之间的数据交流和事件传递。在 Vue 3 中提供了多种方式来实现组件通信,包括:

  • props
  • $emit
  • ref 和 defineExpose
  • provide 和 inject
  • v-model
  • pinia
  • mitt

1. props

父组件将数据传递给子组件,子组件通过props属性接收父组件传递的数据。

子组件 Child.vue:

xml 复制代码
<script setup>
  // 使用 defineProps 函数来定义 Props 的类型和默认值
  // defineProps 不需要引入即可直接使用
  const props = defineProps({
    // 变量 count 是通过父组件传递过来的
    count: {
      type: Number,
      default: 0
    }
  })
</script>

<template>
  <div id="child">
    <h1>count: {{ count }}</h1>
  </div>
</template>

上面这段代码中,使用 defineProps 函数来定义 props 的类型和默认值。

  • 在 defineProps 的参数中,我们可以定义一个对象,其中的每个属性代表一个 Prop。
  • 在这个示例中,我们定义了一个名为 count 的 prop,它的类型是 Number,并设置了默认值为 0。
  • 然后在 template 中,我们可以直接使用 count 变量,它是通过父组件传递过来的。

父组件 Parent.vue:

xml 复制代码
<script setup>
  // 引入 ref 函数,用于定义响应式数据
  import { ref } from 'vue';
  // 引入子组件 Child.vue
  import Child from './Child.vue';

  // 使用 ref 函数创建了一个响应式的变量 count,初始值为 0,该变量将用于传递给子组件
  let count = ref(0)
</script>

<template>
  <div id="parent">
    <!-- 将 count 变量传递给子组件 Child -->
    <Child :count="count"/>
  </div>
</template>

上面这段代码中:

  • 我们使用 ref 函数创建了一个响应式的变量 count,初始值为 0,该变量将用于传递给子组件。
  • 然后在 template 中,通过 :count="count" 的方式将 count 变量传递给子组件 Child。

这样,我们就通过 props 的方式实现了一个最简单的父子组件之间的数据传递。

2. $emit

子组件通过 $emit 方法触发一个自定义事件,并传递需要的参数。父组件通过在子组件上监听对应的事件,并指定触发事件时的回调函数。

子组件 Child.vue:

xml 复制代码
<script setup>
  // 使用 defineProps 函数来定义 Props 的类型和默认值
  // defineProps 不需要引入即可直接使用
  const props = defineProps({
    // 变量 count 是通过父组件传递过来的
    count: {
      type: Number,
      default: 0
    }
  })

  // 使用 defineEmits 函数定义了一个名为 changeParentCount 的自定义事件。
  const emit = defineEmits(['changeParentCount'])
  const changeParentCount = () => {
    // 通过 emit 方法触发名为 changeParentCount 的自定义事件,并将参数 5 传递给父组件。
    emit('changeParentCount', 5)
  }

</script>

<template>
  <div id="child">
    <h1>count: {{ count }}</h1>
    <button @click="changeParentCount">更新父组件的count</button>
  </div>
</template>

上面这段代码中:

  • 我们在 template 的 button 中使用 @click="changeParentCount" 添加点击事件监听器,当按钮被点击时,将调用 changeParentCount 方法,触发父组件中的自定义事件。
  • 然后使用 defineEmits 函数定义了一个名为 changeParentCount 的自定义事件。然后通过 emit 方法触发名为 changeParentCount 的自定义事件,并将参数 5 传递给父组件。

父组件 Parent.vue:

xml 复制代码
<script setup>
  // 引入 ref 函数,用于定义响应式数据
  import { ref } from 'vue';
  // 引入子组件 Child.vue
  import Child from './Child.vue';

  // 使用 ref 函数创建了一个响应式的变量 count,初始值为 0,该变量将用于传递给子组件
  let count = ref(0)

  // 这个方法用于处理子组件中触发的自定义事件 changeParentCount,并更新父组件中的 count 变量的值。
  const changeParentCount = (params) => {
    count.value += params
  }
</script>

<template>
  <div id="parent">
    <!-- 将 count 变量传递给子组件 Child -->
    <!-- 监听子组件自定义事件 changeParentCount -->
    <Child :count="count" @changeParentCount="changeParentCount"/>
  </div>
</template>

上面这段代码中:

  • 在 template 中,我们通过 @ 符号监听子组件自定义事件 changeParentCount,并在父组件中执行名为 changeParentCount 的方法。它接收一个名为 params 的参数,然后更新父组件中的 count 变量的值。

在这个示例中,当子组件触发 changeParentCount 自定义事件时,父组件可以接收到传递的参数5,然后每点击一次子组件的 button 按钮,父组件的 count 的值都递增 5。

这种方式可以通过自定义事件实现子组件向父组件传递数据,并在父组件中处理数据更新的逻辑。

3. ref 和 defineExpose

在 Vue3 中,ref 函数除了可以用于定义一个响应式的变量或引用之外,还可以获取DOM组件实例。

而 defineExpose 是用于将组件内部的属性和方法暴露给父组件或其他组件使用。通过这种方式,我们可以定义哪些部分可以从组件的外部访问和调用。

子组件 Child.vue:

xml 复制代码
<script setup>
  // 引入 ref 函数,用于定义响应式数据
  import { ref } from 'vue';

  // 定义变量和方法
  const msg = ref('我是子组件中的数据');
  const childMethod = () => {
    console.log('我是子组件中的方法');
  }

  // defineExpose 对外暴露组件内部的属性和方法,不需要引入,直接使用
  // 将属性 msg 和方法 childMethod 暴露给父组件
  defineExpose({
    msg,
    childMethod
  })
</script>

上面这段代码中:

  • 我们定义了一个 msg 变量和 一个 childMethod 方法。
  • 然后使用 defineExpose 函数将 msg 和 childMethod 对外暴露出去。

这样,我们在父组件中就可以访问子组件的 msg 属性或者调用 childMethod 方法。

父组件 Parent.vue:

xml 复制代码
<script setup>
  // 引入响应式ref
  import { ref } from 'vue';
  // 引入子组件 Child.vue
  import Child from './Child.vue';

  // 获取子组件DOM实例
  const childRef = ref();

  // 该方法用于获取子组件对外暴露的属性和方法
  const getChildPropertyAndMethod = () => {
    // 获取子组件对外暴露的属性
    console.log(childRef.value.msg);
    // 调用子组件对外暴露的方法
    childRef.value.childMethod();
  }
</script>

<template>
  <div id="parent">
    <Child ref="childRef"/>
    <button @click="getChildPropertyAndMethod">获取子组件对外暴露的属性和方法</button>
  </div>
</template>

上面这段代码中:

  • 我们在 template 的子组件 Child 身上绑定了一个 ref,然后通过 const childRef = ref() 来获取子组件的 DOM 实例。
  • 然后给 button 按钮绑定了一个点击事件 getChildPropertyAndMethod,该方法用于获取子组件对外暴露的属性和方法。
  • 此时,点击按钮,在控制台中,我们就可以看到打印出两句话。

总结:这种方式之间的通信,主要是在子组件内部,将属性和方法暴露出去,然后在子组件中,先获取到子组件的DOM实例,然后就可以访问子组件的属性和调用子组件的方法了。

4. provide 和 inject

在 Vue 3 中,我们可以使用 provide 和 inject 实现跨组件的通信。

  • provide 是在父组件中定义的方法,用于提供数据给所有子组件。 它接收两个参数,第一个参数是一个字符串或者一个 Symbol 类型的键,用于识别提供的数据。第二个参数是要提供的数据本身。这个数据可以是响应式的对象、响应式的 ref、reactive 对象、函数等。父组件中使用 provide 提供数据后,所有的子组件都可以通过 inject 来访问这些数据。
  • inject 是在子组件中使用的方法,用于接收父组件提供的数据。 它接收一个参数,即要注入的数据的键。在子组件中使用 inject 时,可以直接使用接收到的数据,而不需要在组件的配置选项中声明这些数据。

组件 Parent.vue:

xml 复制代码
<script setup>
  // 引入 ref 函数,用于定义响应式数据
  // 引入 provide,用于提供数据给所有子组件
  import { ref, provide } from 'vue';
  // 引入子组件1和子组件2
  import Child1 from './Child1.vue';
  import Child2 from './Child2.vue';

  // 定义一个 message 响应式数据
  const message = ref('我是父组件的数据')

  // 使用 provide 将数据 message 提供给所有子组件
  provide('message', message)

</script>

<template>
  <div id="parent">
    <Child1 />
    <Child2 />
  </div>
</template>

组件 Child1.vue:

xml 复制代码
<script setup>
  import { inject } from 'vue';
	// 使用 inject 获取来自父组件的数据 message
  const parentMessage = inject('message');
</script>

<template>
  <div id="child">
    <p>子组件1: {{ parentMessage }}</p>
  </div>
</template>

组件 Child2.vue:

xml 复制代码
<script setup>
  import { inject } from 'vue';
	// 使用 inject 获取来自父组件的数据 message
  const parentMessage = inject('message');
</script>

<template>
  <div id="child">
    <p>子组件2: {{ parentMessage }}</p>
  </div>
</template>

上面的代码中:

  • 我们在父组件 Parent.vue 中定义了一个响应式数据 message
  • 然后使用 provide 将数据 message 提供给所有子组件
  • 在子组件 Child1 和 Child2 中,我们使用 inject 获取来自父组件的数据 message。
  • 此时,在页面中,我们可以看到子组件获取到的父组件数据。

除了获取数据,我们同样也可以更改数据。

xml 复制代码
<script setup>
  import { inject } from 'vue';

  // 使用 inject 获取来自父组件的数据 message
  const parentMessage = inject('message');

  // 该方法用于更改父组件的message
  const changeParentMessage = () => {
    parentMessage.value = '我更改了message值'
  }
</script>

<template>
  <div id="child">
    <p>子组件1: {{ parentMessage }}</p>
    <button @click="changeParentMessage">更改父组件message</button>
  </div>
</template>

上面这段代码中:

  • 在子组件 Child1 中,我们定义了一个 changeParentMessage 函数,它更新了来自父组件的 message 值。
  • 由于 message 在父组件中是响应式的,所以更新后该值将自动反映在父组件的视图中。
  • 此时,我们点击一下按钮,子组件1和子组件2的值都会被更改。

总结:通过使用 provide 和 inject,数据能够在父组件和子组件之间进行传递和共享,实现了跨组件的通信。

5. v-model

v-model 可以同时支持多个数据双向绑定。

子组件 Child.vue:

xml 复制代码
<script setup>
  const emit = defineEmits(['name', 'age']);
  
  const changeParentMsg = () => {
    emit('update:name', 'Steven')
    emit('update:age', 36)
  }
</script>

<template>
  <div id="child">
    <button @click="changeParentMsg">更新父组件中的name和age</button>
  </div>
</template>

父组件 Parent.vue:

xml 复制代码
<script setup>
  // 引入 ref 函数,用于定义响应式数据
  import { ref } from 'vue';
  // 引入子组件
  import Child from './Child.vue';

  // 定义两个响应式的变量
  let name = ref('Echo');
  let age = ref(26);

</script>

<template>
  <div id="parent">
    <p>父组件name: {{ name }}</p>
    <p>父组件age: {{ age }}</p>
    <!-- 使用 v-model 将父组件的 name 和 age 双向绑定到子组件的 name 和 age 上。 -->
    <Child v-model:name="name" v-model:age="age"/>
  </div>
</template>

上面的代码中:

  • 我们在父组件内部使用 ref 函数定义了两个响应式变量 name 和 age,并给它们分别赋予初始值。然后在 template 中使用 v-model 将父组件的 name 和 age 双向绑定到子组件的 name 和 age 上。
  • 在子组件内部,通过 defineEmits(['name', 'age']),我们定义了两个事件:update:name 和 update:age。这样,父组件可以监听并处理这两个事件。然后我们在 template 中定义了一个按钮,并在 script 中实现 changeParentMsg 的方法。当按钮被点击时,这个方法会调用 emit 方法来派发两个事件。
  • 通过 emit('update:name', 'Steven'),我们触发了一个名为 update:name 的事件,并传递了一个参数 'Steven'。通过这个事件,我们可以告知父组件更新它的 name 值为 'Steven'。
  • 通过 emit('update:age', 36),我们触发了一个名为 update:age 的事件,并传递了一个参数 36。通过这个事件,我们可以告知父组件更新它的 age 值为 36。

通过这样的设置,当父组件中的 name 或 age 发生变化时,它们会自动更新到子组件中。同时,当子组件中的 name 或 age 改变时,它们会通过 update:name 和 update:age 事件反馈给父组件,父组件会相应地更新自己的 name 和 age。这就实现了父子组件之间的双向绑定。

6. pinia

pinia 是一个为 vue3 设计的状态管理库,类似 Vuex 的设计模式,通过定义 store、状态、getter 和 action,来统一管理应用程序的状态和逻辑。

关于pinia的用法,可以看我之前发过的一篇文章,里面讲解的挺详细:

🍍新手入门Pinia状态管理库,看完这篇文章就够了

7. mitt

在 Vue 3 中,可以使用第三方库 mitt 实现组件之间的通信。mitt 是一个简单且强大的事件总线库(类似于 Vue 2 中的EventBus),它提供了一种方便的方式来在不同组件之间传递事件和数据。

下面我们以一个简单的示例来看下 mitt 是如何实现组件通信的:

首先,先安装 mitt.js

csharp 复制代码
yarn add mitt

接着,创建一个 event bus:

javascript 复制代码
// mitt/index.js
import mitt from 'mitt';
const bus = mitt();
export default bus;

在需要通信的组件中,导入 event bus 对象并进行事件的监听和触发:

组件 First.vue:

xml 复制代码
<script setup>
  import mitt from '../mitt';

  const emitEvent = () => {
    mitt.emit('updateName', 36);
  }
</script>

<template>
  <div id="first">
    <button @click="emitEvent">更新name和age</button>
  </div>
</template>

组件 Second.vue:

xml 复制代码
<script setup>
  import mitt from '../mitt';
  import { ref } from 'vue';

  let name = ref('Echo');
  let age = ref(26);

  mitt.on('updateName', (data) => {
    name.value = 'Steven';
    age.value = data;
  });
</script>

<template>
  <div id="second">
    <p>name: {{ name }}</p>
    <p>age: {{ age }}</p>
  </div>
</template>

上面这个例子中:

  • 我们创建了一个名为 mitt 的事件总线对象,并在两个组件中进行了引用。
  • 在 First 组件中,当按钮被点击后,我们使用 mitt.emit 方法触发了一个自定义事件,并传递了一些数据。
  • 在 Second 组件中,我们使用 mitt.on 方法监听了 updateName 事件,并在回调函数中接收到了传递的数据。然后我们将接收到的数据赋值给相应的属性,在模板中展示出来。

通过这种方式,我们可以在不同的组件中实现通信,First 组件可以通过事件总线发送事件和数据,Second 组件则监听事件并接收到数据进行处理。

以上就是我对于Vue3组件通信的一些理解,写的不好,欢迎大家指正!

相关推荐
四岁半儿2 小时前
常用css
前端·css
你的人类朋友3 小时前
说说git的变基
前端·git·后端
姑苏洛言3 小时前
网页作品惊艳亮相!这个浪浪山小妖怪网站太治愈了!
前端
字节逆旅3 小时前
nvm 安装pnpm的异常解决
前端·npm
Jerry3 小时前
Compose 从 View 系统迁移
前端
GIS之路4 小时前
2025年 两院院士 增选有效候选人名单公布
前端
四岁半儿4 小时前
vue,H5车牌弹框定制键盘包括新能源车牌
前端·vue.js
烛阴4 小时前
告别繁琐的类型注解:TypeScript 类型推断完全指南
前端·javascript·typescript
gnip4 小时前
工程项目中.env 文件原理
前端·javascript
JefferyXZF4 小时前
Next.js Server Actions 详解: 无缝衔接前后端的革命性技术(八)
前端·全栈·next.js