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/…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

华为云AI开发平台ModelArts
华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...