【Vue3组件间通信理解】

Vue3组件通信方式

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

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

方式一:props

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

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

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

方式二:自定义事件

可以实现子传父

  1. 注意区分好:原生事件、自定义事件。
  • 原生事件:
    • 事件名是特定的(clickmouseenter等等)
    • 事件对象$event: 是包含事件相关信息的对象(pageXpageYtargetkeyCode
  • 自定义事件:
    • 事件名是任意名称
    • 事件对象$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>
相关推荐
来杯@Java4 小时前
学生选课管理系统(基于springboot+vue前后端分离的项目)计算机毕业设计java
java·spring boot·spring·vue·毕业设计·maven·mybatis
医疗信息化王工12 小时前
医院自律端系统——预警处置模块全栈实战(ASP.NET Core + Vue3 + Quartz 定时调度)
mysql·postgresql·vue·asp.net core·quartz
大大杰哥14 小时前
Vue2学习(1)--了解基本方法与概念
javascript·学习·vue
Agatha方艺璇1 天前
前端开发技术复习笔记
vue·bootstrap·css3·html5·web
小葛要努力2 天前
创建vue2项目
程序人生·vue
七仔啊2 天前
基于海康门禁的人员计数系统
vue
步十人3 天前
【Vue3】前置知识简单概述(包括ES6核心语法,模块化ESM以及npm基础)
arcgis·npm·vue·es6
有梦想的程序星空3 天前
【环境配置】Vue3项目离线化本地部署echarts全攻略
前端·javascript·vue·echarts
向日的葵0064 天前
vue路由(二)
前端·javascript·vue.js·vue
小妖6664 天前
Hydration completed but contains mismatches
javascript·vue·vuepress