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') //解绑指定事件
})
相关推荐
初遇你时动了情40 分钟前
JS中defineProperty/Proxy 数据劫持 vue3/vue2双向绑定实现原理,react 实现原理
javascript·vue.js·react.js
阿华的代码王国1 小时前
【Android】RecyclerView实现新闻列表布局(1)适配器使用相关问题
android·xml·java·前端·后端
汪子熙1 小时前
Angular 最新的 Signals 特性详解
前端·javascript
Spider_Man1 小时前
前端路由双雄传:Hash vs. History
前端·javascript·html
南方kenny1 小时前
CSS Grid 布局:从入门到精通,打造完美二维布局
前端·javascript·css
小泡芙丫1 小时前
从买房到代码:发布订阅模式的"房产中介"之旅
前端·javascript
企鹅吧1 小时前
前端导出 pdf 与 跑马灯效果 最佳实践
前端·javascript·vue.js
南方kenny1 小时前
移动端适配的利器:lib-flexible 原理与实战
前端·javascript·react.js
沫小北1 小时前
HarmonyOS 自定义日期选择器组件详解
前端