Vue2之2组件通信

article/2025/6/24 10:32:22

文章目录

  • 什么是组件通信
  • 不同的组件关系 和 组件通信方案分类
    • 组件关系分类:
    • 组件通信方案分类
  • 父子通信流程图:
    • 父传子
    • 子传父
    • 非父子通信 (拓展) - event bus 事件总线
    • 非父子通信 (拓展) - provide & inject
  • 深入学习prop属性
    • prop接收多个值
    • props 是只读的
    • props 校验
      • props 的 required 必填项
      • props 的 default 默认值
      • props 的 type 值类型
      • 自定义校验validator属性
  • prop & data、单向数据流
    • 注意 我遇到一个Bug大家可以注意一下。

什么是组件通信

组件通信, 就是指 组件与组件 之间的数据传递。

  • 组件的数据是独立的,无法直接访问其他组件的数据。
  • 想用其他组件的数据怎么办呢? → 答:组件通信

在这里插入图片描述

不同的组件关系 和 组件通信方案分类

组件关系分类:

  1. 父子关系
  2. 非父子关系
    在这里插入图片描述

组件通信方案分类

对于父子关系使用 props 和 $emit

非父子关系 1. provide & inject2. eventbus 两种解决方案

对于很复杂的场景比如(爷爷孙子通信):使用Vuex

在这里插入图片描述

父子通信流程图:

  1. 父组件通过 props 将数据传递给子组件
  2. 子组件利用 $emit 通知父组件修改更新
    在这里插入图片描述

父传子

在这里插入图片描述
运行效果
在这里插入图片描述

子传父

在这里插入图片描述

运行效果
在这里插入图片描述
点击按钮后,子组件改变父组件的值,父组件回过来影响子组件的值。

在这里插入图片描述

<!-- 子组件 -->
<template><div>Son - {{ title }}<!-- 定义触发事件名,父组件要和这个保持一致 --><button @click="changeTitle(NewTitle)">Change Title</button></div>
</template><script>
export default {props: ['title'],data () {return {// 子组件内部数据NewTitle: '子传父消息更新子父组件!!'}},methods: {changeTitle (args) {// $emit(触发事件, 传递参数)this.$emit('changeTitle', args)}}
}
</script>
<style></style>
<!-- 父组件 -->
<template><div>father <!-- @子组件触发事件名="父组件方法名" --><Son :title="msg" @changeTitle="handleChange"></Son></div>
</template>
<script>
import Son from './components/Son.vue'
export default {data(){return{msg:'父传子的信息',}},methods:{// 父组件方法(子组件传来的参数)handleChange(NewTitle){this.msg = NewTitle}},components:{Son,}
}
</script>
<style></style>

非父子通信 (拓展) - event bus 事件总线

作用:非父子组件之间,进行简易消息传递。(复杂场景 → Vuex)

  1. 创建一个都能访问到的事件总线 (空 Vue 实例) → utils/EventBus.js

    import Vue from 'vue'
    const Bus = new Vue()
    export default Bus
    
  2. A 组件(接收方),监听 Bus 实例的事件,

    created () {Bus.$on('sendMsg', (msg) => {this.msg = msg})
    }
    
  3. B 组件(发送方),触发 Bus 实例的事件

    Bus.$emit('sendMsg', '这是一个消息')
    

在这里插入图片描述
代码目录
在这里插入图片描述

代码演示

// EventBus.js
import Vue from 'vue'
const Bus  =  new Vue()
export default Bus
<template>
<!-- App.vue --><div class="app"><BaseA></BaseA><BaseB></BaseB><BaseC></BaseC></div>
</template><script>
import BaseA from './components/BaseA.vue'
import BaseB from './components/BaseB.vue'
import BaseC from './components/BaseC.vue'
export default {components:{BaseA,BaseB,BaseC}
}
</script><style></style>
<!-- BaseA.vue -->
<template><div class="base-a">我是A组件(接受方)<p>{{msg}}</p>  </div>
</template><script>
import Bus from '../utils/EventBus'
export default {data() {return {msg: '',}},created() {Bus.$on('sendMsg', (msg) => {// console.log(msg)this.msg = msg})},
}
</script><style scoped>
.base-a {width: 200px;height: 200px;border: 3px solid #000;border-radius: 3px;margin: 10px;
}
</style>
<!-- BaseB.vue -->
<template><div class="base-b"><div>我是B组件(发布方)</div><button @click="sendMsgFn">发送消息</button></div>
</template><script>
import Bus from '../utils/EventBus'
export default {methods: {sendMsgFn() {Bus.$emit('sendMsg', '今天天气不错,适合旅游')},},
}
</script><style scoped>
.base-b {width: 200px;height: 200px;border: 3px solid #000;border-radius: 3px;margin: 10px;
}
</style>
<!-- BaseC.vue -->
<template><div class="base-c">我是C组件(接受方)<p>{{msg}}</p>  </div>
</template><script>
import Bus from '../utils/EventBus'
export default {data() {return {msg: '',}},created() {Bus.$on('sendMsg', (msg) => {// console.log(msg)this.msg = msg})},
}
</script><style scoped>
.base-c {width: 200px;height: 200px;border: 3px solid #000;border-radius: 3px;margin: 10px;
}
</style>

运行结果
在这里插入图片描述
点击发送消息
在这里插入图片描述

非父子通信 (拓展) - provide & inject

provide & inject 作用:跨层级共享数据。

  1. 父组件 provide 提供数据
    export default {provide () {return {// 普通类型【非响应式】color: this.color, // 复杂类型【响应式】userInfo: this.userInfo, }}
    }
    
  2. 子/孙组件 inject 取值使用
    export default {inject: ['color','userInfo'],created () {console.log(this.color, this.userInfo)}
    }
    

在这里插入图片描述

讲解案例目录
在这里插入图片描述
讲解案例代码

<template>
<!-- App.vue --><div class="app">我是APP组件<button @click="change">修改数据</button><SonA></SonA><SonB></SonB></div>
</template><script>
import SonA from './components/SonA.vue'
import SonB from './components/SonB.vue'
export default {provide() {return {// 简单类型 是非响应式的color: this.color,// 复杂类型 是响应式的userInfo: this.userInfo,}},data() {return {color: 'pink',userInfo: {name: '张三',age: 18,},}},methods: {change() {this.color = 'red'this.userInfo.name = '李四'this.userInfo.age = 20},},components: {SonA,SonB,},
}
</script><style>
.app {border: 3px solid #000;border-radius: 6px;margin: 10px;
}
</style>
<template>
<!-- SonA.vue --><div class="SonA">我是SonA组件<GrandSon></GrandSon></div>
</template><script>
import GrandSon from '../components/GrandSon.vue'
export default {components:{GrandSon}
}
</script><style>
.SonA {border: 3px solid #000;border-radius: 6px;margin: 10px;height: 200px;
}
</style>
<template>
<!-- SonB.vue --><div class="SonB">我是SonB组件</div>
</template><script>
export default {}
</script><style>
.SonB {border: 3px solid #000;border-radius: 6px;margin: 10px;height: 200px;
}
</style>
<template>
<!-- GrandSon.vue --><div class="grandSon">我是GrandSon{{ color }} -{{ userInfo.name }} -{{ userInfo.age }}</div>
</template><script>
export default {inject: ['color', 'userInfo'],
}
</script><style>
.grandSon {border: 3px solid #000;border-radius: 6px;margin: 10px;height: 100px;
}
</style>

运行结果
在这里插入图片描述
点击修改数据
在这里插入图片描述

深入学习prop属性

什么是 prop
Prop 定义:组件上 注册的一些 自定义属性
Prop 作用:向子组件传递数据

特点:

  • 可以 传递 任意数量 的prop
  • 可以 传递 任意类型 的prop

prop接收多个值

<template><div><h3>子组件</h3><p>姓名:{{username}}</p><p>年龄:{{age}}</p><p>是否单身:{{isSingle?'是':'否'}}</p><p>汽车:{{"车类型:" + car.name + ",  价格:" + car.price}}</p><p>爱好:{{hobby[0] + "," + hobby[1] + "," + hobby[2]}}</p></div>
</template><script>
export default {props: ['username','age','isSingle', 'car','hobby'],}
</script>
<style></style>
<template><div><h1>父组件</h1><Son:username="username":age="age":isSingle="isSingle":car="car":hobby="hobby"></Son></div>
</template>
<script>
import Son from './components/Son.vue'
export default {data(){return{username:'小帅',age:18,isSingle:true,car: {name:'奔驰',color:'red',price:100000},hobby:['吃饭','睡觉','打豆豆'],}},components:{Son,}
}
</script>
<style></style>

在这里插入图片描述
在这里插入图片描述

props 是只读的

在这里插入图片描述
结果如下
在这里插入图片描述
点击增加
在这里插入图片描述

[Vue warn]: Avoid mutating a prop directly since the value 
will be overwritten whenever the parent component re-renders. 
Instead, use a data or computed property based on the prop's value. 
Prop being mutated: "count"

直接修改 props 是​​高风险行为​​,尽管在简单场景下可能暂时“有效”,但会导致数据流混乱和潜在 Bug。下面打开调试工具发现,虽然子组件更新了但是父组件并没有改变,存在风险。可以通过子传父进行修改。
在这里插入图片描述
在这里插入图片描述

props 校验

思考:组件的 prop 可以乱传么?
作用:为组件的 prop 指定验证要求,不符合要求,控制台就会有错误提示 → 帮助开发者,快速发现错误
语法:
① 类型校验
② 非空校验
③ 默认值
④ 自定义校验

语法1

props: {校验的属性名: 类型 // Number String Boolean ...
},

语法2

props: {校验的属性名: {type: 类型, // Number String Boolean ...required: true, // 是否必填  默认是非必选。default: 默认值, // 默认值validator (value) {// 自定义校验逻辑return 是否通过校验}}
},

props 的 required 必填项

<template><div><h1>父组件 </h1><Son></Son></div>
</template>
<script>
import Son from './components/Son.vue'
export default {data(){return{count:1,}},components:{Son,}
}
</script>
<style></style>
<template><div><h3>子组件</h3>{{ count }}</div>
</template>
<script>
export default {props: {count: {type: Number,default: 1,required: true,validator: function (value) {if (value < 0) {console.error("count不能小于0");return false} else {return true}}}},
}
</script>
<style></style>

在这里插入图片描述

运行结果
在这里插入图片描述
在这里插入图片描述

props 的 default 默认值

当上面的必填项为false时,此时默认值是1;
在这里插入图片描述
运行结果
在这里插入图片描述

props 的 type 值类型

在这里插入图片描述

运行结果
在这里插入图片描述

自定义校验validator属性

在这里插入图片描述
结果
在这里插入图片描述

prop & data、单向数据流

共同点:都可以给组件提供数据。
区别:

  • data 的数据是自己的 → 随便改
  • prop 的数据是外部的 → 不能直接改,要遵循 单向数据流

单向数据流:父级 prop 的数据更新,会向下流动,影响子组件。这个数据流动是单向的。口诀:谁的数据谁负责这就是我们上面讲的如何结果prop的可读但不可修改的问题。
在这里插入图片描述

<template><div><h3>子组件</h3><button @click="sub">-1</button>{{ count }}<button @click="add">+1</button></div>
</template>
<script>
export default {props: {count: {type: Number,default: 1,required: false,validator: function (value) {if (value < 0) {console.error("count不能小于0");return false} else {return true}}}},methods: {sub(){// 这里不能直接修改props的值,需要通过事件通知父组件this.$emit('sub', this.count-1)},add(){// 这里不能直接修改props的值,需要通过事件通知父组件this.$emit('add', this.count+1)}}
}
</script>
<style></style>
<template><div><h1>父组件 </h1><!-- 父级 prop 的数据更新,会向下流动,影响子组件。这个数据流动是单向的。 --><Son :count="fcount" @sub="subCount" @add="addCount"></Son></div>
</template>
<script>
import Son from './components/Son.vue'
export default {data(){return{fcount: 0,}},methods:{subCount(newCount){this.fcount=newCountconsole.log(newCount);},addCount(newCount){this.fcount=newCount}},components:{Son,}
}
</script>
<style></style>

注意 我遇到一个Bug大家可以注意一下。

这个我遇到一个bug,大家可以看看为什么我点击+1或者-1不生效。

<template><div><h3>子组件</h3><button @click="sub">-1</button>{{ newCount }}<button @click="add">+1</button></div>
</template>
<script>
export default {props: {count: {type: Number,default: 1,required: false,validator: function (value) {if (value < 0) {console.error("count不能小于0");return false} else {return true}}}},data() {return {newCount: this.count}},methods: {sub(){this.$emit('sub', this.newCount - 1)},add(){this.$emit('add', this.newCount + 1)}}
}
</script>
<style></style>
<template><div><h1>父组件 </h1><Son :count="fcount" @sub="subCount" @add="addCount"></Son></div>
</template>
<script>
import Son from './components/Son.vue'
export default {data(){return{fcount: 0,}},methods:{subCount(newCount){this.fcount=newCount},addCount(newCount){this.fcount=newCount}},components:{Son,},components:{Son,}
}
</script>
<style></style>

问题核心分析
​​子组件 newCount 未同步更新​​

  • ​​现象​​:父组件通过 :count=“fcount” 传递数据给子组件,但子组件只在初始化时通过 data() { newCount: this.count } 赋值一次。当父组件更新 fcount 时,子组件的 newCount ​​不会自动更新​​。
  • ​​后果​​:点击按钮时,子组件基于旧值计算 newCount±1,导致父组件接收到的值始终滞后。

结果办法

<template><div><h3>子组件</h3><button @click="sub">-1</button>{{ newCount }}<button @click="add">+1</button></div>
</template>
<script>
export default {props: {count: {type: Number,default: 1,required: false,validator: function (value) {if (value < 0) {console.error("count不能小于0");return false} else {return true}}}},data() {return {// 父组件 默认让上面prop中的值变化。但是不会更新这里newCount的值。// 也就是说父组件更新 fcount=1,会使prop中的值更新,但子组件 newCount 仍为 0;newCount: this.count}},methods: {sub(){// 先改变自己的数据,然后再去改变父组件的数据this.newCount = this.newCount - 1this.$emit('sub', this.newCount)},add(){// 先改变自己的数据,然后再去改变父组件的数据this.newCount = this.newCount + 1this.$emit('add', this.newCount)}}
}
</script>
<style></style>
<template><div><h1>父组件 </h1><Son :count="fcount" @sub="subCount" @add="addCount"></Son></div>
</template>
<script>
import Son from './components/Son.vue'
export default {data(){return{fcount: 0,}},methods:{subCount(newCount){this.fcount=newCount},addCount(newCount){this.fcount=newCount}},components:{Son,}
}
</script>
<style></style>

http://www.hkcw.cn/article/jmnSsSAUZr.shtml

相关文章

Qt -下载Qt6与OpenCV

博客主页&#xff1a;【夜泉_ly】 本文专栏&#xff1a;【暂无】 欢迎点赞&#x1f44d;收藏⭐关注❤️ 前言 呃啊&#xff0c;本来就想在 Qt 里简单几个 OpenVC 的函数&#xff0c;没想到一搞就是一天。 我之前的开发环境是 Qt 5.14.2&#xff0c;使用 MinGW 7.3.0 64-bit 编…

8088单板机C语言sprintf()格式化串口输出---Prj04

#include "tiny_stdarg.h" // 使用自定义可变参数实现#define ADR_273 0x0200 #define ADR_244 0x0400 #define LED_PORT 0x800 #define PC16550_THR 0x1f0 #define PC16550_LSR 0x1f5 / //基本的IO操作函数 / char str[]"Hello World! 20250531 Ve…

HTML实现端午节主题网站:龙舟争渡,凭吊祭江诵君赋。

名人说&#xff1a;龙舟争渡&#xff0c;助威呐喊&#xff0c;凭吊祭江诵君赋。——苏轼《六幺令天中节》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、项目概览&#xff1a;传统与现代的技术碰撞1. 核心特…

YOLOv10改进|爆改模型|涨点|在颈部网络添加结合部分卷积PConv和SDI融合方法的PSDI特征融合层(附代码+修改教程)

一、文本介绍 本文修改的模型是YOLOv10&#xff0c;YOLOv10无需非极大值抑制&#xff08;NMS&#xff09;进行后处理&#xff0c;其推理速度以及参数量上都优于现有的模型。然而&#xff0c;针对某些目标检测任务中需要同时处理多尺度目标的挑战&#xff0c;YOLOv10 在此类场景…

Redis最佳实践——安全与稳定性保障之高可用架构详解

全面详解 Java 中 Redis 在电商应用的高可用架构设计 一、高可用架构核心模型 1. 多层级高可用体系 #mermaid-svg-Ffzq72Onkv7wgNKQ {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Ffzq72Onkv7wgNKQ .error-icon{f…

虚拟存储器:将十六进制逻辑地址 0A5C、103C、1A5C 转换成物理地址(2)

转换成十进制&#xff08;分步骤解析&#xff09; 确定页号和偏移的计算方式 页大小1KB 2^10&#xff0c;逻辑地址中 页号 逻辑地址 1024&#xff08;整数除法&#xff09;&#xff0c;页内偏移 逻辑地址 % 1024。物理地址 物理块号 1024 页内偏移&#xff0c;其中物理块…

【HTML】基础学习【数据分析全栈攻略:爬虫+处理+可视化+报告】

- 第 102 篇 - Date: 2025 - 05 - 31 Author: 郑龙浩/仟墨 文章目录 HTML 基础学习一 了解HTML二 HTML的结构三 HTML标签1 标题2 文本段落3 换行4 加粗、斜体、下划线5 插入图片6 添加链接7 容器8 列表9 表格10 class类 HTML 基础学习 一 了解HTML 一个网页分为为三部分&…

吴恩达MCP课程(2):research_server

目录 代码代码解释导入模块常量定义MCP服务器初始化工具函数定义1. search_papers 函数2. extract_info 函数 主程序总结 运行示例 代码 import arxiv import json import os from typing import List from mcp.server.fastmcp import FastMCPPAPER_DIR "papers"mc…

【数据结构】——二叉树--链式结构

一、实现链式结构二叉树 二叉树的链式结构&#xff0c;那么从名字上我们就知道我们这个二叉树的底层是使用链表来实现的&#xff0c;前面我们的二叉树是通过数组来实现的&#xff0c;那么在其是完全二叉树的情况下&#xff0c;此时我们使用数组来实现就会使得其空间浪费较少&a…

netty中的EventLoop原理解析

一起来学netty 1. EventLoop的基本概念2. EventLoop的核心组件3. EventLoop的工作流程4. EventLoop与Channel的关系5. EventLoop的实现类6. EventLoop的线程模型7. EventLoop的优点8. EventLoop的注意事项9. 示例代码10.异步编程模型解析异步编程的定义异步编程的核心特点异步编…

使用Java实现简单的计算机案例

第一个案例我决定做一个简单的“简易计算器”&#xff0c;来开启编程之旅。为什么我会选择这个案例来作为第一个Java案例呢&#xff1f;大家可别小看这个小小的计算器&#xff0c;它既简单又实用。通过这个案例&#xff0c;大家可以学会或着练习如何处理用户输入、如何实现基本…

流媒体基础分析:延迟分析与安全性保障

在流媒体传输过程中&#xff0c;延迟和安全性是两个至关重要的方面。它们直接影响着用户的观看体验和内容的版权保护。本文将深入分析延迟的来源与追赶技术&#xff0c;并探讨流媒体传输的安全性保障手段。 1. 延迟分析 1.1 延迟说明 延迟是流媒体传输中不可避免的问题&#…

S32K3 工具篇9:如何在无源码情况下灵活调试elf文件

S32K3 工具篇9&#xff1a;如何在无源码情况下灵活调试elf文件 一&#xff0c;文档简介二&#xff0c; 功能实现2.1 代码工具准备2.2 elf修改功能实现&#xff1a;Fun2功能跳过2.2.1 PC越过Fun22.2.2 Fun2替换为nop 2.3 elf修改功能实现&#xff1a;Fun4替换Fun2入口2.3.1 link…

树莓派PWM控制LED灯

目录 一、什么是PWM二、树莓派引脚图三、命令行控制LED灯四、PWM控制LED呼吸灯 一、什么是PWM PWM&#xff08;Pulse Width Modulation&#xff0c;脉冲宽度调制&#xff09;是一种通过调节数字信号的占空比&#xff08;Duty Cycle&#xff09;来模拟模拟信号的技术。它通过快…

第十四章 MQTT订阅

系列文章目录 系列文章目录 第一章 总体概述 第二章 在实体机上安装ubuntu 第三章 Windows远程连接ubuntu 第四章 使用Docker安装和运行EMQX 第五章 Docker卸载EMQX 第六章 EMQX客户端MQTTX Desktop的安装与使用 第七章 EMQX客户端MQTTX CLI的安装与使用 第八章 Wireshark工具…

六.MySQL增删查改

CRUD : Create(创建), Retrieve(读取)&#xff0c;Update(更新)&#xff0c;Delete&#xff08;删除&#xff09; 一.增 insert 1.单行数据 全列插入 语法特点&#xff1a;不指定字段名&#xff0c;按表结构字段顺序依次提供所有值。 注意&#xff1a;字段顺序必须与表定义一…

TKernel模块--自定义RTTI,对象句柄,引用计数

TKernel模块–RTTI&#xff0c;对象句柄&#xff0c;引用计数 1.DEFINE_STANDARD_HANDLE(x1, x2) #define DEFINE_STANDARD_HANDLE(C1,C2) DEFINE_STANDARD_HANDLECLASS(C1,C2,Standard_Transient)其中&#xff1a; #define DEFINE_STANDARD_HANDLECLASS(C1,C2,BC) class C1…

关于TongWeb数据源兼容mysql驱动的注意事项

问题现象&#xff1a; TongWeb数据源在采用mysql驱动的国产数据库时&#xff0c;因数据库慢报超时为数据源配置参数的 validation-query-timeout值5秒&#xff0c;而不是期望的maxwait、connectiontimeout值。 The last packet successfully received from the server was 5,0…

CSS专题之水平垂直居中

前言 石匠敲击石头的第 16 次 在日常开发中&#xff0c;经常会遇到水平垂直居中的布局&#xff0c;虽然现在基本上都用 Flex 可以轻松实现&#xff0c;但是在某些无法使用 Flex 的情况下&#xff0c;又应该如何让元素水平垂直居中呢&#xff1f;这也是一道面试的必考题&#xf…

(新)MQ高级-MQ的可靠性

消息到达MQ以后&#xff0c;如果MQ不能及时保存&#xff0c;也会导致消息丢失&#xff0c;所以MQ的可靠性也非常重要。 一、数据持久化 为了提升性能&#xff0c;默认情况下MQ的数据都是在内存存储的临时数据&#xff0c;重启后就会消失。为了保证数据的可靠性&#xff0c;必须…