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智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving
地址:LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂,正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...