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') //解绑指定事件
})
相关推荐
橙子家28 分钟前
浏览器缓存之【结构化数据库与缓存】: IndexedDB、Cache storage 和 Storage buckets
前端
user205855615181334 分钟前
X6 中边悬浮置顶,规避 `mouseleave` 事件丢失问题
前端
李明卫杭州35 分钟前
CSS aspect-ratio 属性完全指南
前端
Pedantic3 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘3 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆3 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师4 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆4 小时前
VSCode自动格式化三要素
前端
爱勇宝5 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员