Vue3 组件数据传输小全

Vue3 组件的数据传输

方法目录

  • 方式一:defineProps(父传子)
  • 方式二:defineEmits(子传父)
  • 方式三:v-model(父子双向绑定)
  • 方式四:Pinia
  • 方式五:provide/inject(依赖注入, 适合祖孙组件通信 )
  • 方式六:mitt库

方式一:父传子(props)

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

父组件

xml 复制代码
<template>
  <child-component :message="parentMessage" />
</template>
​
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'
​
let parentMessage = ref('111')
</script>

子组件

xml 复制代码
<template>
  <div>{{ message }}</div>
</template>
​
<script setup>
defineProps(['message'])
</script>

方式二、子传父(emits)

父组件: 定义一个函数,参数为子组件中传递的数据,自定义事件名,并将函数传给子组件。

xml 复制代码
<template>
  <child-component @send-data="getSonData"/>
</template>
​
<script setup>
import ChildComponent from './ChildComponent.vue'
​
// 定义一个函数,用于接收子组件中的数据
function getSonData(data){
 console.log("子组件中的数据:",data)
}
</script>

子组件: defineEmits声明事件,调用父组件中声明的事件回调函数,并把数据当做参数传入。emit函数第一个参数为事件回调函数名,后续参数为该回调函数的参数。

xml 复制代码
<template>
  <button @click="emit('send-data',data)">把数据发送到父组件</button>
</template>
​
<script setup>
import { ref } from 'vue'
​
let data = ref('111') // 子组件中的数据
const emit = defineEmits(['send-data']) // 接收props
</script>

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

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

父组件

xml 复制代码
<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>

子组件

xml 复制代码
<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>

方式四、使用 Pinia 进行组件通信

场景 1:跨组件共享状态(如用户登录信息)

(1)定义Store
typescript 复制代码
// stores/user.ts
import { defineStore } from 'pinia'
​
export const useUserStore = defineStore('user', {
  state: () => ({
    name: 'Anonymous',
    isLogin: false
  }),
  actions: {
    login(name: string) {
      this.name = name
      this.isLogin = true
    },
    logout() {
      this.name = 'Anonymous'
      this.isLogin = false
    }
  }
})
(2) 组件A(修改状态)
xml 复制代码
<!-- ComponentA.vue -->
<script setup>
import { useUserStore } from '@/stores/user'
​
const user = useUserStore()
</script>
​
<template>
  <button @click="user.login('Alice')">登录</button>
</template>
(3) 组件B(读取状态)
xml 复制代码
<!-- ComponentB.vue -->
<script setup>
import { useUserStore } from '@/stores/user'
​
const user = useUserStore()
</script>
​
<template>
  <div v-if="user.isLogin">欢迎, {{ user.name }}!</div>
</template>

场景2 :兄弟组件间通信

(1) 定义 Store
typescript 复制代码
// stores/cart.ts
import { defineStore } from 'pinia'
​
export const useCartStore = defineStore('cart', {
  state: () => ({
    items: [] as { id: number; name: string }[]
  }),
  actions: {
    addItem(item: { id: number; name: string }) {
      this.items.push(item)
    },
    removeItem(id: number) {
      this.items = this.items.filter(item => item.id !== id)
    }
  }
})
(2)组件A(添加商品)
xml 复制代码
<!-- ComponentA.vue -->
<script setup>
import { useCartStore } from '@/stores/cart'
​
const cart = useCartStore()
</script>
​
<template>
  <button @click="cart.addItem({ id: 1, name: '商品A' })">加入购物车</button>
</template>
(2)组件B(显示购物车)
xml 复制代码
<!-- ComponentB.vue -->
<script setup>
import { useCartStore } from '@/stores/cart'
​
const cart = useCartStore()
</script>
​
<template>
  <div v-for="item in cart.items" :key="item.id">
    {{ item.name }}
    <button @click="cart.removeItem(item.id)">删除</button>
  </div>
</template>

场景 3:复杂状态逻辑(如异步请求)

(1)定义Store
typescript 复制代码
// stores/posts.ts
import { defineStore } from 'pinia'
​
export const usePostStore = defineStore('posts', {
  state: () => ({
    posts: [] as { id: number; title: string }[],
    loading: false
  }),
  actions: {
    async fetchPosts() {
      this.loading = true
      const res = await fetch('https://api.example.com/posts')
      this.posts = await res.json()
      this.loading = false
    }
  }
})
(2)组件调用
xml 复制代码
<!-- PostList.vue -->
<script setup>
import { usePostStore } from '@/stores/posts'
​
const postStore = usePostStore()
// 首次加载数据
postStore.fetchPosts()
</script>
​
<template>
  <div v-if="postStore.loading">加载中...</div>
  <ul v-else>
    <li v-for="post in postStore.posts" :key="post.id">
      {{ post.title }}
    </li>
  </ul>
</template>

方式五、依赖注入(provie、inject)

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

爷组件

xml 复制代码
<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>

父组件

xml 复制代码
<template>
  <div class="page">
    <threePage />
  </div>
</template>
<script setup lang="ts">
import threePage from "./threePage.vue";
</script>

子组件

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

方式六、mitt(),不大建议使用,建议用Pinia

全局事件总线mitt库(父子通信,兄弟通信都可,兼容vue2、vue3)

mitt 是一个 极简的事件总线库 (约 200 bytes),用于组件间的 跨层级通信,适用于:

  • 非父子组件通信(兄弟组件、祖孙组件等)
  • 简单场景下的全局事件监听/触发
  • 替代 Vue 2 的 $emit/$on(Event Bus 模式)
安装
复制代码
npn install mitt
封装JS文件,方便调用
javascript 复制代码
import mitt from 'mitt'
const $emmiter = mitt()
export default $emmiter
使用

emitter实例中有四个方法,分别为emit「触发事件」、on「绑定事件」、off「解绑事件」、all「所有绑定事件」。 接收数据方:定义事件及回调函数

父组件A.vue

xml 复制代码
<template>
  <div>
    <B></B>
    <C></C>
    A接收到的信息:{{ thing }}
  </div>
</template>
​
<script setup>
  import B from './B.vue'
  import C from './C.vue'
  import {ref} from 'vue'
  import $mitter from '../../core/eventbus'
​
  let thing = ref('')
  $mitter.on('send-data', (value)=>{
​
    thing.value=value;
  })
</script>

子组件B.vue

xml 复制代码
<template>
  <div id="pageB">
    <button @click="handeler">B点击</button>
  </div>
</template>
<script setup>
  import {ref} from 'vue'
  import $mitter from '../../core/eventbus';
​
  let data = ref('bbbb11111');
​
  function handeler(){
    $mitter.emit('send-data', data.value)
  }
</script>

子组件C.vue

xml 复制代码
<template>
  <div id="pageC">
    <span>C接收到:{{thing}}</span>
  </div>
</template>
<script setup>
  import {ref} from 'vue'
  import $mitter from '../../core/eventbus';
  let thing = ref('');
  $mitter.on('send-data', (value)=>{
    thing.value=value;
  })
</script>

注意:接收数据方,需要在组件卸载时,及时解绑事件,避免内存泄漏。

javascript 复制代码
import {onUnmounted} from 'vue'
onUnmounted(()=>{
    $eminter.off('send-data') //解绑指定事件
})
相关推荐
崔庆才丨静觅1 天前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 天前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 天前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 天前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 天前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 天前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 天前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅1 天前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊1 天前
jwt介绍
前端
爱敲代码的小鱼1 天前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax