前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue 3 组件通信方式总结

Vue 3 组件通信方式总结

原创
作者头像
iwhao
发布2024-07-24 12:28:14
750
发布2024-07-24 12:28:14

用vue3开发前端项目的话,组件通信则是必修课,方式一般有 以下这几种

  • Props(自定义属性)
  • 自定义事件
  • v-model(算是Props和自定义事件的结合,只不过属性和事件名称是默认设置的)
  • Provide & Inject
  • attrs
  • ref、parent
  • 全局状态管理(如 Pinia 或Vuex)
  • 事件总线

常用的大概就以上这么多,下面将针对这几种方法来展开详细的重点说明和实际应用。

props

父级组件用法与vue2相同

代码语言:javascript
复制
<template>
  <div class="parent">
    <div class="title">
      <h1>父组件</h1>
    </div>
    <child :count="count"></child>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import child from './child.vue'
let count = ref(0)
</script>

子组件接收父级自定义属性则与vue2不同,vue2中是以props 配置项来接收,vue3 中则需要 用到宏函数 defineProps类接收

代码语言:javascript
复制
<template>
  <div class="child">
    <h1>子组件</h1>
    <h1>父级props:{{ count }}</h1>
  </div>
</template>

<script setup>
defineProps(['count'])

</script>

如果想给count 设置类型和默认值,和vue2设置方法差不多一样

代码语言:javascript
复制
defineProps({
  count: {
    type: Number,
    default: 1
  }
})

自定义事件

父级组件用法与vue2相同

父组件

代码语言:javascript
复制
<template>
  <div class="parent">
    <div class="title">
      <h1>父组件: {{ count }}</h1>
      <h1 v-show="childCount">子组件给的值:{{ childCount }}</h1>
    </div>
    <child :count="count" @send-add="addCount"></child>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import child from './child.vue'

let count = ref(0)
let childCount = ref(0)

function addCount(value) {
  childCount.value = value
}
</script>

子组件的化就不能用vue2中的\$emit了,需要换成宏函数 defineEmits,参数为数组,数组的元素为父级的 自定义事件名称 sendAdd,defineEmits返回的值一个对象,该对象包含了组件可以触发的所有自定义事件

代码语言:javascript
复制
<template>
  <div class="child">
    <h1>子组件</h1>
    <h1>父组件给的值:{{ count }}</h1>
    <button @click="emit('sendAdd',2)">CountAdd</button>
  </div>
</template>

<script setup>
defineProps({
  count: {
    type: Number,
    default: 1
  }
})

let emit = defineEmits(['sendAdd'])
</script>

v-model

  • 在vue2中 v-model其实是 prop为 value和 自定义事件是 input 的语法糖
  • 在vue3中同样,只不过 value 变为了 modelValue,input改成了update\:modelValue 而且子组件中接收 需要用到宏函数defineModel

还有一点不同的是就是 vue2中只能绑定一个v-model,但vue3中却可以绑定多个

父组件 写法还是v-model

vue版本 3.4之前的写法

用defineProps接受modelValue,defineEmits返回

父组件

代码语言:javascript
复制
<template>
  <div class="parent">
    <div class="title">
      <h1>父组件 count: {{ count }}</h1>
    </div>
    <child v-model="count"></child>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import child from './child.vue'
let count = ref(0)
</script>

子组件

代码语言:javascript
复制
<template>
  <div class="child">
    <h1>子组件</h1>
    <button @click="emit('update:modelValue',20)">CountAdd</button>
  </div>
</template>

<script setup>
defineProps(['modelValue'])
let emit = defineEmits(['modelValue'])
</script>

从vue 版本3.4后 加入了宏函数 defineModel,支持以下两种写法

第一种单个v-model

defineModel会默认接收

代码语言:javascript
复制
<template>
  <div class="parent">
    <div class="title">
      <h1>父组件</h1>
    </div>
    <child v-model="count"></child>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import child from './child.vue'
let count = ref(0)
</script>

子组件中 defineModel接收父级v-model,默认的prop为 "modelValue,

返回的是一个ref,通过修改这个ref的Value,自动触发 update\:modelValue 事件,这样就不用再使用defineEmits来触发了了,真的很方便

代码语言:javascript
复制
<template>
  <div class="child">
    <h1>子组件</h1>
    <h1>父组件给的值:{{ modelValue }}</h1>
    <button @click="sendAdd">CountAdd</button>
  </div>
</template>

<script setup>

let count = defineModel()

function sendAdd(){
  count.value = 20
}
</script>

还支持多个 v-model

代码语言:javascript
复制
<!--父-->
<template>
  <div class="parent">
    <div class="title">
      <h1>父组件 name: {{ name }}</h1>
      <h1>父组件 count: {{ count }}</h1>
    </div>
    <child v-model:name="name" v-model:count="count"></child>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import child from './child.vue'
let count = ref(0)
let name = ref('父组件')
</script>

代码语言:javascript
复制
<!--子-->
<template>
  <div class="child">
    <h1>子组件</h1>
    <button @click="sendAdd">CountAdd</button>
    <button @click="changeName">changeName</button>
  </div>
</template>

<script setup>
let count = defineModel('count')
let name = defineModel('name')
function sendAdd(){
  count.value = 20
}
function changeName(){
  name.value = '子组件'
}
</script>

另外 defineModel 还是支持设置默认值和类型

代码语言:javascript
复制
defineModel('count', { required: 10,type: Number,req })

下面是效果图

单个v-model

多个v-model

attrs

父给孙传参数

下方的 v-bind="{ money: money, spend: spend }\

等价于 \:money="money" \:spend="spend"

父组件

代码语言:javascript
复制
<template>
  <div class="parent">
    <div class="title">
      <h1>父组件</h1>
      <h1>我的钱: {{ money }} 百万</h1>
    </div>
    <!-- <child :count="count" :money="money"></child> -->
    <child v-bind="{ money: money, spend: spend }"></child>
  </div>
</template>

<script setup>
import { ref } from "vue";
import child from "./child.vue";

let money = ref(100);
let spend = (num) => {
  money.value -= num
}
</script>

子组件 相当于中间过度,把props 里没有接收参数,以\$attrs传给孙子组件

代码语言:javascript
复制
<template>
  <div class="child">
    <h1>子组件</h1>
    <grandson v-bind="$attrs"></grandson>
  </div>
</template>

<script setup>
import grandson from './grandson.vue'

</script>

孙组件

代码语言:javascript
复制
<template>
  <div class="grandson">
    <h4>孙组件</h4>
    <h4>得到的钱: {{ money }} 百万</h4>
    <button @click="spend(1)">消费</button>
  </div>
</template>

<script setup>
defineProps(['money','spend'])

</script>

Provide & Inject

provide 和 inject 通常会在不同的组件中运行,最顶层组件 provide提供数据(依赖注入),所有子组件用 inject(注入) 来接收,

降低耦合度、提高可重用性、易于管理、功能增强

父组件

代码语言:javascript
复制
<template>
  <div class="parent">
    <div class="title">
      <h1>父组件</h1>
      <h1>我的钱: {{ money }} 百万</h1>
    </div>
    <child></child>
  </div>
</template>

<script setup>
import { ref, provide } from 'vue';
import child from './child.vue'

let money = ref(100)

provide('money', money)

provide('spend', (num) => {
  money.value -= num
})
</script>

子组件

代码语言:javascript
复制
<template>
  <div class="child">
    <h1>子组件</h1>
    <grandson></grandson>
  </div>
</template>

<script setup>

import grandson from './grandson.vue'

</script>

孙组件

代码语言:javascript
复制
<template>
  <div class="grandson">
    <h4>孙组件</h4>
    <h4>得到的钱: {{ money }} 百万</h4>
    <button @click="spend(1)">消费</button>
  </div>
</template>

<script setup>
import { inject } from 'vue'
let money = inject('money')
let spend = inject('spend')
</script>

inject 第二个参数还能设置默认值,当provide没有以来注入'money'这个关键字时,默认值才会生效

代码语言:javascript
复制
inject('money',200)

ref、parent

ref写法不变,但获取组件实例的方法有所改动,组件上ref绑定一个关键字(cc),js中 需要声明一个ref绑定的关键字的同名字 let cc = ref(),通过cc就能访问到组件的实例

父组件

代码语言:javascript
复制
<template>
  <div class="parent">
    <div class="title">
      <h1>父组件</h1>
      <h1>父亲的钱:{{ count }}</h1>
      <button @click="reduce">减少孩子的钱</button>
    </div>
    <child ref="cc"></child>
  </div>
</template>

<script setup>
import { ref } from "vue";
import child from "./child.vue";
let cc = ref();
let count = ref(1000);
let reduce = () => {
  console.log(cc.value);
  cc.value.money -= 1;
  count.value += 1;
};
<!--defineExpose({count})-->
</script>

子组件

代码语言:javascript
复制
<template>
  <div class="child">
    <h1>子组件</h1>
    <h1>name: {{ name }}</h1>
    <h1>age: {{ age }}</h1>
    <h1>money: {{ money }}</h1>
    <button @click="add($parent)">要回自己的钱</button>
  </div>
</template>

<script setup>
    import { ref } from "vue";
    
    let name = ref("我是子组件");
    let age = ref(18);
    let money = ref(500);
    let add = (parent) => {
      console.log(parent)
      money.value += 1;
      parent.count -= 1;
    };
    <!--defineExpose({ money });-->
</script>

点击按钮打印实例会发现 实例中没有子组件的数据源,那是因为vue 3 加入了 defineExpose。需要子组件 在defineExpose中声明 被允许访问的数据源,没被声明的将无法被访问。

子组件中 加入以下代码 money 才能被访问

代码语言:javascript
复制
defineExpose({money})

同样父组件中声明被允许访问的数据源头

代码语言:javascript
复制
defineExpose({count})

pinia

pinane

可以理解为下一代 vuex,作者也称之为vuex5,同时vue已经将 pinia 收入 官方账户了

具体使用方法请移步这里我之前有总结过 的 Vue3(ts)中使用 pinia

事件总线 mitt

安装 mitt 库:

代码语言:javascript
复制
npm install mitt

然后,创建一个事件总线的模块 eventBus.js:

代码语言:javascript
复制
import mitt from'mitt';

const eventBus = mitt();

export default eventBus;

在需要发送事件的组件中:

代码语言:javascript
复制
import eventBus from './eventBus';

// 发送事件
eventBus.emit('myEvent', { data: '这是事件携带的数据' });

在需要接收事件的组件中:

代码语言:javascript
复制
import eventBus from './eventBus';

// 监听事件
eventBus.on('myEvent', (payload) => {
  console.log('接收到事件:', payload);
});

这样就实现了一个基本的 事件总线,用于组件之间的通信。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • props
  • 自定义事件
  • v-model
    • vue版本 3.4之前的写法
      • 第一种单个v-model
        • 还支持多个 v-model
        • attrs
        • Provide & Inject
        • ref、parent
        • pinia
        • 事件总线 mitt
        相关产品与服务
        事件总线
        腾讯云事件总线(EventBridge)是一款安全,稳定,高效的云上事件连接器,作为流数据和事件的自动收集、处理、分发管道,通过可视化的配置,实现事件源(例如:Kafka,审计,数据库等)和目标对象(例如:CLS,SCF等)的快速连接,当前 EventBridge 已接入 100+ 云上服务,助力分布式事件驱动架构的快速构建。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档