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组件通信的一些理解,写的不好,欢迎大家指正!

相关推荐
丁总学Java31 分钟前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
懒羊羊大王呀42 分钟前
CSS——属性值计算
前端·css
xgq1 小时前
使用File System Access API 直接读写本地文件
前端·javascript·面试
道爷我悟了1 小时前
Vue入门-指令学习-v-html
vue.js·学习·html
无咎.lsy1 小时前
vue之vuex的使用及举例
前端·javascript·vue.js
fishmemory7sec1 小时前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
fishmemory7sec1 小时前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
工业互联网专业2 小时前
毕业设计选题:基于ssm+vue+uniapp的校园水电费管理小程序
vue.js·小程序·uni-app·毕业设计·ssm·源码·课程设计
豆豆2 小时前
为什么用PageAdmin CMS建设网站?
服务器·开发语言·前端·php·软件构建
计算机学姐2 小时前
基于SpringBoot+Vue的在线投票系统
java·vue.js·spring boot·后端·学习·intellij-idea·mybatis