【Vue3组件间通信理解】

Vue3组件通信方式

总结来源于 尚硅谷vue3
大部分实现双向通信都是向子组件传递一个函数,在函数中修改。总结写在前头:

  • 任意间组件通信: mitt
  • 若想实现父子间双向通信
    • props
    • v-model
    • provide, inject
    • refs, parent组合使用,不如上面的方便
  • 若实现单向通信
    • 子到父
      • 自定义事件
      • parent
    • 父到子
      • refs
    • 祖-> 孙
      • provide, inject
      • $attrs, 需要父组件参与且父组件一旦获取某个值,一般情况下子组件不能通过这种方式再获取了

方式一:props

Props可以实现 父->子, 子->父

  • 若 父传子 :属性值是非函数。
  • 若 子传父 :属性值是函数。

使用到的红函数: defineProps('xx')

方式二:自定义事件

可以实现子传父

  1. 注意区分好:原生事件、自定义事件。
  • 原生事件:
    • 事件名是特定的(click、mouseenter等等)
    • 事件对象$event: 是包含事件相关信息的对象(pageX、pageY、target、keyCode)
  • 自定义事件:
    • 事件名是任意名称
    • 事件对象$event: 是调用emit时所提供的数据,可以是任意类型!!!

demo
<Child @xxx = "function / 表达式"/>

这里相当于在Child组件上绑定了一个事件xxx,子组件可以通过:
const emits = defineEmits(['xx'])获取自定的事件.然后 emits('xx', 相关数据)将数据传到回调函数function中

方式三:mitt

可以实现任意组件间的通信

可以理解成一个第三方自定义事件仓库(自己向里面放)

  1. 下载mitt,打开终端(vscode中按ctrl + ~), npm i mitt
  2. 在src文件下载新建工具包utils, 新建emitter.ts文件
  3. 创建mitt
typescript 复制代码
// 引入mitt 
import mitt from "mitt";

// 创建emitter
const emitter = mitt()

// 创建并暴露mitt
export default emitter
  1. 使用
typescript 复制代码
// 绑定事件
emitter.on('send-toy',(value)=>{
  console.log('send-toy事件被触发',value)
})

onUnmounted(()=>{
  // 解绑事件
  emitter.off('send-toy')
})

// 需要传数据的vue文件中写如下代码
function sendToy(){
  // 触发事件
  emitter.emit('send-toy',toy.value)
}

Notice:记得在组件销毁前解绑相关事件

方式四:v-model

v-model放在组件标签上也可以时间父子间双向通信,一般组件库常用这种方法

html 复制代码
<!--
子组件是一个自定义的input组件,里面放着自己写的input结构和样式
-->
<Child v-model="username"/>

<!-- 上面情况等价于 -->
<Child :username="username" 
@update:modelValue="username=$emit" />

Chile.vue

html 复制代码
<template>
  <div class="box">
    <!--将接收的value值赋给input元素的value属性,目的是:为了呈现数据 -->
		<!--给input元素绑定原生input事件,触发input事件时,进而触发update:model-value事件-->
    <input 
       type="text" 
       :value="modelValue" 
       @input="emit('update:model-value',$event.target.value)"
    >
  </div>
</template>

<script setup lang="ts" name="AtguiguInput">
  // 接收props
  defineProps(['modelValue'])
  // 声明事件
  const emit = defineEmits(['update:model-value'])
</script>

如果不想使用modelValue这个标识符,可以这样:v-model:xx,这里的xx就代替了modelValue, 这种情况下你可以传递多个值

方式五:$attrs

实现 祖->孙 数据传递

本质上就是祖->父, 父->子,只不过这个过程父亲不能接受数据

祖
v-bind="{x:100,y:200} === :x="100" :y="200"

html 复制代码
<template>
  <div class="father">
    <h3>父组件</h3>
		<Child :a="a" :b="b" :c="c" :d="d" v-bind="{x:100,y:200}" :updateA="updateA"/>
  </div>
</template>

父

html 复制代码
<h3>子组件</h3>
<!-- 祖传的数据父亲不用,都存在$attrs上了,可以借助vue开发者工具查看 -->
<GrandChild v-bind="$attrs"/>

孙

html 复制代码
<template>
	<div class="grand-child">
		<h3>孙组件</h3>
		<h4>a:{{ a }}</h4>
		<h4>b:{{ b }}</h4>
		<h4>c:{{ c }}</h4>
		<h4>d:{{ d }}</h4>
		<h4>x:{{ x }}</h4>
		<h4>y:{{ y }}</h4>
		<button @click="updateA(666)">点我更新A</button>
	</div>
</template>

<script setup lang="ts" name="GrandChild">
	defineProps(['a','b','c','d','x','y','updateA'])
</script>

方式六:refs, parent

可以实现父子间通信

  • $refs获取所有ref标识的子组件的信息(包括数据),前提是子组件通过defineExpose将数据暴露出去
  • $parent获取当前组件的所有父组件的信息(包括数据),前提是父组件通过defineExpose将数据暴露出去
$refs 值为对象,包含所有被ref属性标识的DOM元素或组件实例。
$parent 值为对象,当前组件的父组件实例对象。

方式七:provide, inject

实现祖<->孙间通信

  • 不需要经过父组件的插手
  • 在祖先组件中通过provide配置向后代组件(不一定只是孙, 父,曾孙等都可以)提供数据
  • 在后代组件中通过inject配置来声明接收数据

祖

html 复制代码
<template>
  <div class="father">
    <h3>父组件</h3>
    <h4>资产:{{ money }}</h4>
    <h4>汽车:{{ car }}</h4>
    <button @click="money += 1">资产+1</button>
    <button @click="car.price += 1">汽车价格+1</button>
    <Child/>
  </div>
</template>

<script setup lang="ts" name="Father">
  import Child from './Child.vue'
  import { ref,reactive,provide } from "vue";
  // 数据
  let money = ref(100)
  let car = reactive({
    brand:'奔驰',
    price:100
  })
  // 用于更新money的方法
  function updateMoney(value:number){
    money.value += value
  }
  // 提供数据
  provide('moneyContext',{money,updateMoney})
  provide('car',car)
</script>

孙
注意:孙组件在接受的时候需要额外设置默认值,不然ts会警告

html 复制代码
<template>
  <div class="grand-child">
    <h3>我是孙组件</h3>
    <h4>资产:{{ money }}</h4>
    <h4>汽车:{{ car }}</h4>
    <button @click="updateMoney(6)">点我</button>
  </div>
</template>

<script setup lang="ts" name="GrandChild">
  import { inject } from 'vue';
  // 注入数据
 let {money,updateMoney} = inject('moneyContext',{money:0,updateMoney:(x:number)=>{}})
  let car = inject('car')
</script>
相关推荐
落花看风雪2 天前
从零开始搭建后台系统(vue3+TypeScript+Vite)01
typescript·vue·js
那年那棵树6 天前
基于WebGis的人生如旅地图系统
前端·arcgis·vue·课程设计
冒险的豆子9 天前
Vue2 、Vue3 响应式对比及优劣
vue
NetX行者10 天前
用 Vite + Vue3 + UnoCSS 搭建企业级项目模板(一):基础骨架
vue·vite·unocss
java水泥工10 天前
什么是前端
java·vue·html5
mont evergreen11 天前
一封从外部世界寄来的信:二维码数据如何穿越三重门,抵达你的屏幕
electron·vue·事件
志尊宝12 天前
Vue3 零基础每日笔记(071):暗黑模式与主题切换——CSS 变量 + Element Plus 暗黑方案
前端·css·笔记·vue·html·前端开发·软件开发
怦然丶心动12 天前
用 Java 把干支历法算清楚:从节气切年到可读摘要
java·算法·vue·独立开发·spring boot / spring
我最爱吃鱼香茄子14 天前
【毕业设计优选】人力资源管理系统|Java|SpringBoot|Vue|前后端分离|带详细文档+部署教学视频
java·vue·毕业设计·springboot
骑着蜗牛撵大象32715 天前
SpringBoot+Vue3 企业智能体侧挂架构:独立服务、独立数据库与主线零侵入落地
数据库·spring boot·架构·vue·springboot·事件驱动·服务拆分