当前位置: 首页 > news >正文

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&#xff0c;props / $emit1.1&#xff0c;一个需求方法1方法2 1.2&#xff0c;v-model 和 .syncv-model.sync 2&#xff0c;$children / $parent3&#xff0c;ref4&#xff0c;$attrs / $listeners$attrs$listenersinheritAttrs1.1 的问题的第3种解决方法 5&#xff0c;…...

Flutter flutter.minSdkVersion的实际文件位置

Flutter 项目的Android相关版本号配置&#xff1a; flutter.minSdkVersion 的版本号配置文件实际路径&#xff1a; …/flutter_sdk/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy Flutter版本号如下&#xff1a; bzbMacBook-Pro ccsmec % flutter --version …...

python生成PDF报告

前言 最近接到了一个需求-将项目下的样本信息汇总并以PDF的形式展示出来&#xff0c;第一次接到这种PDF的操作的功能&#xff0c;还是有点慌的&#xff0c;还好找到了reportlab这个包&#xff0c;可以定制化向PDF写内容&#xff01; 让我们由简入深进行讲解 一、reportlab是…...

在visual studio里安装Python并创建python工程

在2009年&#xff0c;云计算开始发力&#xff0c;Python、R、Go这些天然处理批量计算的语言也迅猛发展。微软在2010年&#xff0c;把Python当成一个语言包插件&#xff0c;集成到了visual studio 2010里。在"云优先&#xff0c;移动优先"的战略下&#xff0c;于2015年…...

AIGC(生成式AI)试用 6 -- 从简单到复杂

从简单到复杂&#xff0c;这样的一个用例该如何设计&#xff1f; 之前浅尝试用&#xff0c;每次尝试也都是由浅至深、由简单到复杂。 一点点的“喂”给生成式AI主题&#xff0c;以测试和验证生成式AI的反馈。 AIGC&#xff08;生成式AI&#xff09;试用 1 -- 基本文本_Role…...

竞赛 基于深度学习的人脸识别系统

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的人脸识别系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/…...

uniapp:APP开发,后台保活

前言&#xff1a; 在ios中&#xff0c;软件切换至后台、手机息屏&#xff0c;过了十来秒软件就会被系统挂起&#xff0c;APP内的任务就不能继续执行&#xff1b;在android中&#xff0c;默认情况下&#xff0c;软件在后台运行的时候&#xff0c;触发某些特定条件的情况下&…...

vue2 项目中嵌入视频

案例&#xff1a; 代码&#xff1a; <template><div class"schematicDiagramIndex"><el-container><el-aside width"20rem"> <!-- <h4 style"font-size: 18px">视频演示</h4>--><div styl…...

第二章 进程与线程 十二、进程同步与进程互斥

目录 一、进程同步 1、定义 二、进程互斥 1、定义 2、四个部分 3、原则 一、进程同步 1、定义 进程同步是指在多个进程之间协调执行顺序的一种机制&#xff0c;使得进程按照一定的顺序执行&#xff0c;以避免出现不一致的情况。常见的实现方式有信号量、管程、屏障等。…...

Linux内核链表(list)移植到任意平台

一、前言 linux内核链表在include/linux/list.h文件中&#xff0c;内核中实现的链表比较简洁&#xff0c;实用性很强&#xff0c;因此想把它单独移植出来使用。 内核中的代码只能使用gnuc编译器编译&#xff0c;stdc编译器编译是会报错的&#xff0c;主要是因为typeof这个宏是…...

【操作系统】聊聊什么是CPU上下文切换

对于linux来说&#xff0c;本身就是一个多任务运行的操作系统&#xff0c;运行远大于CPU核心数的程序&#xff0c;从用户视角来看是并发执行&#xff0c;而在CPU视角看其实是将不同的CPU时间片进行分割&#xff0c;每个程序执行一下&#xff0c;就切换到别的程序执行。那么这个…...

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 语言和类实现顺序表 属性包括&#xff1a;数组、实际长度、最大长度&#xff08;设定为 1000 &#xff09; 操作包括&#xff1a;创建、插入、删除、查找 类定义参考 #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复选框默认选中样式

问题描述&#xff1a; <input type"checkbox" /> input修改checkbox默认选中样式&#xff0c;直接设置选中后的样式不生效&#xff0c;需要先给复选框设置-webkit-appearance: none&#xff08;取消默认样式&#xff09;&#xff0c; 再设置样式才会生效。 …...

高云FPGA系列教程(10):letter-shell移植

文章目录 letter-shell简介letter-shell源码获取letter-shell移植函数和变量应用示例 本文是高云FPGA系列教程的第10篇文章。 shell&#xff0c;中文是外壳的意思&#xff0c;就是操作系统的外壳。通过shell命令可以操作和控制操作系统&#xff0c;比如Linux中的Shell命令就包括…...

【C语言学习笔记---指针进阶02】

C语言程序设计笔记---017 C语言进阶之回调函数1、函数指针数组2、回调函数3、 回调函数的应用 --- qsort库函数4、模拟qsort函数5、结语 C语言进阶之回调函数 前言&#xff1a; 通过C语言进阶前篇的指针进阶01的知识&#xff0c;继续学习。这篇引用一个简易计算器的程序进行深…...

低功耗蓝牙物联网:未来连接的无限可能

物联网是连接各种设备和传感器的网络&#xff0c;其目的是实现信息的交换和共享&#xff0c;提高效率并优化生活。在这个领域&#xff0c;低功耗蓝牙&#xff08;BLE&#xff09;正在发挥着越来越重要的作用。 低功耗蓝牙是一种无线通信技术&#xff0c;它的主要特点是低功耗和…...

安装社区版本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&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

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 资源 论文绘图神器来了&#xff1a;一行…...

python/java环境配置

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

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

微信小程序云开发平台MySQL的连接方式

注&#xff1a;微信小程序云开发平台指的是腾讯云开发 先给结论&#xff1a;微信小程序云开发平台的MySQL&#xff0c;无法通过获取数据库连接信息的方式进行连接&#xff0c;连接只能通过云开发的SDK连接&#xff0c;具体要参考官方文档&#xff1a; 为什么&#xff1f; 因为…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用

中达瑞和自2005年成立以来&#xff0c;一直在光谱成像领域深度钻研和发展&#xff0c;始终致力于研发高性能、高可靠性的光谱成像相机&#xff0c;为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...