Vue2中10种组件通信方式和实践技巧
目录
- 1,props / $emit
- 1.1,一个需求
- 方法1
- 方法2
- 1.2,v-model 和 .sync
- v-model
- .sync
- 2,$children / $parent
- 3,ref
- 4,$attrs / $listeners
- $attrs
- $listeners
- inheritAttrs
- 1.1 的问题的第3种解决方法
- 5,provide / inject
- 6,Vuex
- 7,EventBus
- 8,dispatch 和 broadcast
- 9,路由
- 10,localStorage / SessionStorage
以组件之间的关系,主要分为3种情况
- 父子组件通信
- 隔代组件通信
- 全局通信(任意组件)
兄弟组件通信:以父级为媒介,就变成父子组件通信。或通过全局通信。
1,props / $emit
这是最基础也最常用的方式。
- 父组件通过
props向子组件传递数据。 - 子组件通过
$emit调用父组件方法向父组件传参。
注意,不应该在子组件中改变 props,这会破坏单向数据流。如果有这样需求,可用
computed做转换。
举例1
<!-- 父组件 -->
<template><Children :count="count" @updateCount="updateCount" />
</template><script>
import Children from "./components/Children.vue";
export default {components: {Children,},data() {return {count: 0,};},methods: {updateCount(num) {this.count = num;},},
};
</script>
<!-- 子组件 -->
<template><div><div>{{ count }}</div><button @click="handleClick">修改 count</button></div>
</template><script>
export default {props: ["count"],methods: {handleClick() {this.$emit("updateCount", 10);},},
};
</script>
1.1,一个需求
子组件调用父组件方法时,需要等待父组件处理之后,再执行子组件其他逻辑。如何实现?
方法1
$emit() 可以传多个参数,所以也可以传递回调函数。实现如下:
<!-- 父组件 -->
<template><Children @updateCount="updateCount" />
</template><script>
import Children from "./components/Children.vue";
// 模拟延迟
const delay = (duration) => {return new Promise((resolve) => {setTimeout(() => {resolve()}, duration)})
}
export default {components: {Children,},data() {return {count: 0,};},methods: {async updateCount(num, callback) {this.count = num;await delay(2000)callback && callback()},},
};
</script>
<!-- 子组件 -->
<template><button @click="handleClick">修改 count</button>
</template><script>
export default {methods: {handleClick() {this.$emit("updateCount", 10, () => {// 等待父组件调用该回调函数后,再执行子组件其他逻辑// ...});},},
};
</script>
方法2
将父组件的方法作为参数传递(而不是事件),子组件中直接使用该属性即可。
<!-- 父组件 -->
<template><Children :updateCount="updateCount" />
</template><script>
import Children from "./components/Children.vue";
// 模拟延迟
const delay = (duration) => {return new Promise((resolve) => {setTimeout(() => {resolve();}, duration);});
};
export default {components: {Children,},data() {return {count: 0,};},methods: {async updateCount(num) {this.count = num;await delay(2000);},},
};
</script>
<!-- 子组件 -->
<template><button @click="handleClick">修改 count</button>
</template><script>
export default {props: {updateCount: Function,},methods: {async handleClick() {await this.updateCount(10);// 等待父组件调用该回调函数后,再执行子组件其他逻辑// ...},},
};
</script>
1.2,v-model 和 .sync
这2个都是语法糖,可以实现数据的双向绑定。但本质上还是
props / $emit。
v-model
参考-自定义事件
当在一个组件上使用 v-model 时, 默认传入名为 value 的 prop 和名为 input 的事件。
<!-- 父组件 -->
<template><Children v-model="count" /><!-- 二者等效 --><!-- <Children :value="count" @input="(newValue) => (count = newValue)" /> -->
</template><script>
import Children from "./components/Children.vue";
export default {components: {Children,},data() {return {count: 0,};},
};
</script>
<!-- 子组件 -->
<template><div><div>{{ value }}</div><button @click="handleClick">修改 count</button></div>
</template><script>
export default {props: ["value"],methods: {handleClick() {this.$emit("input", 123);},},
};
</script>
可以使用 model 来修改 v-model 的默认设置。
<!-- 子组件 -->
<script>
export default {model: {prop: "value1",event: "change",},props: ["value1"],methods: {handleClick() {this.$emit('change', 123)},},
};
</script>
.sync
参考 - .sync 修饰符
本质如下:
<!-- 父组件 -->
<template><!-- <Children :count.sync="count" /> --><!-- 二者相等 --><Children :count="count" @update:count="(newValue) => (count = newValue)" />
</template>
子组件中的处理和 v-model 类似,不做赘述。
2,$children / $parent
$parent 和 $children 都可以访问组件的实例,所以可直接访问和修改属性。
组件的子组件可能有多个,所以
$children是数组,如果组件没有子组件,则$children是空数组。
子组件的父组件只有一个,所以$parent直接就是组件实例,App.vue的父组件是根组件实例$root,在往上就是undefined。
举例:
<!-- 父组件 -->
<template><div><Children :count="count" /><button @click="changeChild">修改子组件的值</button></div>
</template><script>
import Children from './components/Children.vue'
export default {components: {Children},data() {return {count: 0}},methods: {changeChild() {this.$children[0].name = '下雪天的夏风'}}
}
</script>
<!-- 子组件 -->
<template><div><div>{{ count }}</div><div>{{ name }}</div><button @click="changeParent">修改父组件的值</button></div>
</template><script>
export default {props: ['count'],data() {return {name: '子组件'}},methods: {changeParent() {this.$parent.count = 1}}
}
</script>
3,ref
1,称为模板引用,用于获取子组件的实例。在 v-for 中使用时,获取的是一个 ref 数组(注意不保证和源数组顺序相同)。
2,对普通元素使用时,获取的是DOM元素,比 document.querySelector() 更方便。
举例:
<!-- 父组件 -->
<template><div><Children ref="_refChild" /><button @click="changeChild">改变子组件的值</button></div>
</template><script>
import Children from './components/Children.vue'
export default {components: {Children},methods: {changeChild() {this.$refs._refChild.name = '下雪天的夏风'}}
}
</script>
<!-- 子组件 -->
<template><div>{{ name }}</div>
</template><script>
export default {data() {return {name: '子组件'}}
}
</script>
下面是隔代通信
4,$attrs / $listeners
当组件嵌套多层时,使用上面的父子组件通信的方式,略显繁琐。
比如对 A <-B<-C 3个组件,A 是父组件。当C需要使用A组件的属性和方法时,一般处理情况:
-
对属性来说,C 中
prop接收 B 的属性,B 又prop接收 A 的属性。 -
对方法来说,C 中
$emit()B 的方法,B 又$emit()A 的方法。
B 只起了中转的作用,但却需要写重复的代码,属性和方法较多时都就更难受了。
所以出现了 $attrs / $listeners
$attrs
如果子组件没有在 props 中接收父组件传递给它的属性(不包含 class 和 style 属性),则这些属性会放在 $attrs 中
$listeners
包含了父组件传递过来了所有自定义事件 (不包含 .native 修饰器的)。
所以,在中间组件上可用$attrs / $listeners 直接中转属性和方法即可,不用写多余的代码。
举例
<!-- A组件 -->
<template><BComponent :name="name" :age="age" :sex="sex" @clickA="methodA" />
</template><script>
import BComponent from './components/B.vue'
export default {components: {BComponent},data() {return {name: '下雪天的夏风',age: 18,sex: 'male'}},methods: {methodA(item) {console.log(item)}}
}
</script>
<!-- B组件 -->
<template><CComponent v-bind="$attrs" v-on="$listeners" /><!-- 或只传递需要的属性和方法 --><!-- <CComponent v-bind="{ age: $attrs.age }" v-on="{ clickA: $listeners.clickA }" /> -->
</template><script>
import CComponent from './C.vue'
export default {inheritAttrs: false, // 下面有解释props: ['name'], // 则 $attrs 上只有 age 和 sexcomponents: {CComponent}
}
</script>
<!-- C组件 -->
<template><div><div>{{ sex }} {{ age }}</div><button @click="handleClick">触发A组件的方法</button></div>
</template><script>
export default {props: ['age', 'sex'],methods: {handleClick() {this.$emit('clickA', 123)}}
}
</script>
inheritAttrs
表示是否将 $attrs 作为当前组件的根元素上的 HTML 属性,默认 true。
为 true 时,B组件最终渲染的HTML
<div age="18" sex="male"><div>male 18</div><button>触发A组件的方法</button>
</div>
false 时
<div><div>male 18</div><button>触发A组件的方法</button>
</div>
1.1 的问题的第3种解决方法
原问题:子组件调用父组件方法时,需要等待父组件处理之后,再执行子组件其他逻辑。如何实现?
<!-- 父组件 -->
<template><Children @updateCount="updateCount" />
</template><script>
import Children from "./components/Children.vue";
// 模拟延迟
const delay = (duration) => {return new Promise((resolve) => {setTimeout(() => {resolve();}, duration);});
};
export default {components: {Children,},data() {return {count: 0,};},methods: {async updateCount(num) {this.count = num;await delay(2000);},},
};
</script>
<!-- 子组件 -->
<template><button @click="handleClick">修改 count</button>
</template><script>
export default {props: {updateCount: Function,},methods: {async handleClick() {await this.$listeners.updateCount(10);// 等待父组件调用该回调函数后,再执行子组件其他逻辑// ...},},
};
</script>
5,provide / inject
provide / inject需要在一起使用,无视嵌套的层级。
祖先组件通过 provide 提供属性和方法,所有后代组件都可以通过 inject 接收注入的属性和方法。
注意,provide 和 inject 绑定不是响应的!
<!-- 祖先组件 -->
<script>
export default {provide: {author: 'xxx'},
}
</script>
<!-- 某后代组件 -->
<template><div>{{ author }}</div>
</template><script>
export default {inject: ['author'],// 也可以像 props 一样,设置默认值// inject: {// author: {// default: 'fpp'// }// },
}
</script>
下面是全局通信
6,Vuex
不多赘述,官方文档 很详细。
7,EventBus
事件总线
我们需要一个插件满足以下需求,来实现不受层级约束的组件通信。
- 可以监听 / 取消监听某个事件。
- 可以触发监听的事件,还可以传参,并且自动通知监听者。
实现:
// eventBus.js
const listeners = {}export default {// 监听$on(event, handler) {if (!listeners[event]) {listeners[event] = new Set()}// 同一个事件,可以绑定多个处理函数listeners[event].add(handler)},// 取消监听$off(event, handler) {if (listeners[event]) {listeners[event].delete(handler)}},// 触发监听,可传递参数$emit(event, ...args) {if (listeners[event]) {for (const handler of listeners[event]) {handler(...args)}}}
}
举例1
// test.js
import eventBus from './eventBus.js'
eventBus.$on('event1', function () {console.log('1')
})eventBus.$on('event1', function () {console.log('2')
})setTimeout(() => {eventBus.$emit('event1')
}, 1000)
而 Vue 实例本身就实现了上面这3个方法,所以可直接这样实现
// eventBus.js
import Vue from 'vue'
export default new Vue({})
或是直接绑定到原型上来使用。
// main.js
import Vue from 'vue'
import App from './App.vue'const vm = new Vue({render: (h) => h(App)
})Vue.prototype.$bus = vm
vm.$mount('#app')
举例2:下面2个组件层级关系不做限制。
<!-- 组件 1 -->
<template><button @click="handleClick">触发 event1 事件</button>
</template><script>
export default {methods: {handleClick() {this.$bus.$emit('event1', 123)}}
}
</script>
<!-- 组件 2 -->
<script>
export default {methods: {methodC1(item) {console.log(item)},methodC2(item) {console.log(item)}},created() {this.$bus.$on('event1', this.methodC1)this.$bus.$on('event1', this.methodC2)}
}
</script>
注意,这个思路是将事件放到一个对象中管理(监听和触发)。如果直接在不同组件中使用
this.$on()和this.$emit()是无效的,因为this指向不同的组件实例,而$emit()触发的是实例自己通过$on()监听的事件。
8,dispatch 和 broadcast
因为这篇文章太长了,无法发表,所以 看这篇文章
9,路由
通过 url 参数通信。
不多赘述,官方文档 很详细。
10,localStorage / SessionStorage
不多赘述。
以上。
相关文章:
Vue2中10种组件通信方式和实践技巧
目录 1,props / $emit1.1,一个需求方法1方法2 1.2,v-model 和 .syncv-model.sync 2,$children / $parent3,ref4,$attrs / $listeners$attrs$listenersinheritAttrs1.1 的问题的第3种解决方法 5,…...
Flutter flutter.minSdkVersion的实际文件位置
Flutter 项目的Android相关版本号配置: flutter.minSdkVersion 的版本号配置文件实际路径: …/flutter_sdk/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy Flutter版本号如下: bzbMacBook-Pro ccsmec % flutter --version …...
python生成PDF报告
前言 最近接到了一个需求-将项目下的样本信息汇总并以PDF的形式展示出来,第一次接到这种PDF的操作的功能,还是有点慌的,还好找到了reportlab这个包,可以定制化向PDF写内容! 让我们由简入深进行讲解 一、reportlab是…...
在visual studio里安装Python并创建python工程
在2009年,云计算开始发力,Python、R、Go这些天然处理批量计算的语言也迅猛发展。微软在2010年,把Python当成一个语言包插件,集成到了visual studio 2010里。在"云优先,移动优先"的战略下,于2015年…...
AIGC(生成式AI)试用 6 -- 从简单到复杂
从简单到复杂,这样的一个用例该如何设计? 之前浅尝试用,每次尝试也都是由浅至深、由简单到复杂。 一点点的“喂”给生成式AI主题,以测试和验证生成式AI的反馈。 AIGC(生成式AI)试用 1 -- 基本文本_Role…...
竞赛 基于深度学习的人脸识别系统
前言 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习的人脸识别系统 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-senior/…...
uniapp:APP开发,后台保活
前言: 在ios中,软件切换至后台、手机息屏,过了十来秒软件就会被系统挂起,APP内的任务就不能继续执行;在android中,默认情况下,软件在后台运行的时候,触发某些特定条件的情况下&…...
vue2 项目中嵌入视频
案例: 代码: <template><div class"schematicDiagramIndex"><el-container><el-aside width"20rem"> <!-- <h4 style"font-size: 18px">视频演示</h4>--><div styl…...
第二章 进程与线程 十二、进程同步与进程互斥
目录 一、进程同步 1、定义 二、进程互斥 1、定义 2、四个部分 3、原则 一、进程同步 1、定义 进程同步是指在多个进程之间协调执行顺序的一种机制,使得进程按照一定的顺序执行,以避免出现不一致的情况。常见的实现方式有信号量、管程、屏障等。…...
Linux内核链表(list)移植到任意平台
一、前言 linux内核链表在include/linux/list.h文件中,内核中实现的链表比较简洁,实用性很强,因此想把它单独移植出来使用。 内核中的代码只能使用gnuc编译器编译,stdc编译器编译是会报错的,主要是因为typeof这个宏是…...
【操作系统】聊聊什么是CPU上下文切换
对于linux来说,本身就是一个多任务运行的操作系统,运行远大于CPU核心数的程序,从用户视角来看是并发执行,而在CPU视角看其实是将不同的CPU时间片进行分割,每个程序执行一下,就切换到别的程序执行。那么这个…...
CMake教程-第 2 步 添加一个库
CMake教程-第 2 步 添加一个库 1 CMake教程介绍2 学习步骤Step 1: A Basic Starting PointStep 2: Adding a LibraryStep 3: Adding Usage Requirements for a LibraryStep 4: Adding Generator ExpressionsStep 5: Installing and TestingStep 6: Adding Support for a Testin…...
DS 顺序表--类实现(C++数据结构题)
实现顺序表的用 C 语言和类实现顺序表 属性包括:数组、实际长度、最大长度(设定为 1000 ) 操作包括:创建、插入、删除、查找 类定义参考 #include<iostream> using namespace std; #define ok 0 #define error -1 // 顺…...
0.UML
1.图 1.1类图含义 第一层显示类的名称,如果是抽象类,则就用斜体显示。第二层是类的特性,通常就是字段和属性。第三层是类的操作,通常是方法或行为。注意前面的符号, ,表示public,-,表示private,#,表示protected。 1.2接口图 与类图的区别主要是顶端有<< interface >…...
PostgreSQL设置主键为自增
1、创建自增序列 CREATE SEQUENCE table_name_id_seq START 1; 2、设置字段默认值 字段默认值中设置 nextval(table_name_id_seq) 3、常用查询 -- 查询所有序列 select * from information_schema.sequences where sequence_schema public; -- 查询自增序列的当前值 select cu…...
input修改checkbox复选框默认选中样式
问题描述: <input type"checkbox" /> input修改checkbox默认选中样式,直接设置选中后的样式不生效,需要先给复选框设置-webkit-appearance: none(取消默认样式), 再设置样式才会生效。 …...
高云FPGA系列教程(10):letter-shell移植
文章目录 letter-shell简介letter-shell源码获取letter-shell移植函数和变量应用示例 本文是高云FPGA系列教程的第10篇文章。 shell,中文是外壳的意思,就是操作系统的外壳。通过shell命令可以操作和控制操作系统,比如Linux中的Shell命令就包括…...
【C语言学习笔记---指针进阶02】
C语言程序设计笔记---017 C语言进阶之回调函数1、函数指针数组2、回调函数3、 回调函数的应用 --- qsort库函数4、模拟qsort函数5、结语 C语言进阶之回调函数 前言: 通过C语言进阶前篇的指针进阶01的知识,继续学习。这篇引用一个简易计算器的程序进行深…...
低功耗蓝牙物联网:未来连接的无限可能
物联网是连接各种设备和传感器的网络,其目的是实现信息的交换和共享,提高效率并优化生活。在这个领域,低功耗蓝牙(BLE)正在发挥着越来越重要的作用。 低功耗蓝牙是一种无线通信技术,它的主要特点是低功耗和…...
安装社区版本OB
获取一键安装包 https://www.oceanbase.com/softwarecenter 离线安装 [admintest001 ~]$ tar -xzf oceanbase-all-in-one-*.tar.gz [admintest001 ~]$ cd oceanbase-all-in-one/bin/ [admintest001 bin]$ ./install.sh [admintest001 bin]$ source ~/.oceanbase-all-in-one/…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
