Vue 中provide和inject的作用,在什么场景下使用它们进行跨层级组件通信?

大白话Vue 中provide和inject的作用,在什么场景下使用它们进行跨层级组件通信?

在 Vue 里,provideinject 这两个东西可有用啦,它们就像是快递员和收件人,能帮我们在组件之间传递数据。咱们先搞清楚它们的作用,再看看在哪些场景下用它们进行跨层级组件通信。

作用

  • provide:这个就像是快递员发货。在父组件里,你可以用 provide 来提供一些数据或者方法,这些数据和方法就像是包裹,会被送到下面的子组件、孙子组件等等。
  • inject:这就好比收件人取包裹。在子组件或者更深层级的组件里,你可以用 inject 来接收从上面传下来的数据或者方法。

代码示例

下面是一个简单的示例,我会在代码里加上详细的注释,这样你就能更好地理解啦。

<template><!-- 父组件 --><div><h1>父组件</h1><!-- 引入子组件 --><ChildComponent /></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},// 提供数据和方法provide() {return {// 提供一个字符串数据message: '这是从父组件传递下来的消息',// 提供一个方法sayHello: () => {console.log('Hello!');}};}
};
</script>
<template><!-- 子组件 --><div><h2>子组件</h2><!-- 显示从父组件传递下来的消息 --><p>{{ message }}</p><!-- 点击按钮调用从父组件传递下来的方法 --><button @click="sayHello">点击打招呼</button><!-- 引入孙子组件 --><GrandChildComponent /></div>
</template><script>
import GrandChildComponent from './GrandChildComponent.vue';export default {components: {GrandChildComponent},// 注入从父组件传递下来的数据和方法inject: ['message', 'sayHello']
};
</script>
<template><!-- 孙子组件 --><div><h3>孙子组件</h3><!-- 显示从父组件传递下来的消息 --><p>{{ message }}</p><!-- 点击按钮调用从父组件传递下来的方法 --><button @click="sayHello">点击打招呼</button></div>
</template><script>
export default {// 注入从父组件传递下来的数据和方法inject: ['message', 'sayHello']
};
</script>

使用场景

provideinject 适合在跨层级组件通信的场景下使用,比如:

  • 全局配置:当你有一些全局的配置信息,像主题颜色、API 地址之类的,你可以在根组件里用 provide 提供这些配置,然后在下面的各个组件里用 inject 接收。
  • 插件和组件库:在开发插件或者组件库的时候,有些数据或者方法需要在不同层级的组件里共享,这时候就可以用 provideinject

不过要注意哦,provideinject 是单向数据流,也就是说,子组件不能直接修改从父组件传下来的数据。如果需要修改数据,你可以通过事件或者其他方式通知父组件来修改。

在使用provide和inject时,有哪些注意事项?

在使用 provideinject 进行跨层级组件通信时,有以下一些注意事项:

1. 单向数据流

  • provideinject 建立的是单向数据流,从父组件向子组件传递数据。子组件不能直接修改通过 inject 接收到的数据。若子组件需要修改数据,应当通过自定义事件等方式通知父组件进行修改。
<template><div><ChildComponent /></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},provide() {return {value: this.value,// 提供一个修改值的方法updateValue: (newValue) => {this.value = newValue;}};},data() {return {value: 1};}
};
</script>
<template><div><button @click="changeValue">修改值</button></div>
</template><script>
export default {inject: ['value', 'updateValue'],methods: {changeValue() {// 调用父组件提供的方法修改值this.updateValue(this.value + 1);}}
};
</script>

2. 响应式问题

  • 如果 provide 提供的是一个简单数据类型(如字符串、数字、布尔值),当这个数据在父组件中更新时,子组件中通过 inject 接收到的数据不会自动更新。不过,如果提供的是一个响应式对象(如通过 reactive 创建的对象),那么当对象属性发生变化时,子组件能接收到更新。
<template><div><button @click="updateObj">更新对象</button><ChildComponent /></div>
</template><script>
import { reactive } from 'vue';
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},setup() {const obj = reactive({message: '初始消息'});const updateObj = () => {obj.message = '更新后的消息';};return {provide: {sharedObj: obj},updateObj};}
};
</script>
<template><div><!-- 会响应式更新 --><p>{{ sharedObj.message }}</p></div>
</template><script>
export default {inject: ['sharedObj']
};
</script>

3. 命名冲突

  • 要确保 provide 提供的数据和方法的名称不会与子组件内部的属性或方法名冲突。如果存在冲突,可能会导致意外的结果。可以采用命名空间的方式来避免冲突,例如将提供的数据封装在一个对象中。
<template><div><ChildComponent /></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},provide() {return {// 使用命名空间myApp: {message: '这是应用消息',doSomething: () => {console.log('做一些事情');}}};}
};
</script>
<template><div><p>{{ myApp.message }}</p><button @click="myApp.doSomething">执行操作</button></div>
</template><script>
export default {inject: ['myApp']
};
</script>

4. 性能影响

  • 过度使用 provideinject 可能会对性能产生一定影响,尤其是在大型应用中。因为每个使用 inject 的组件都会在实例创建时查找注入的内容。如果在很多组件中频繁使用,会增加组件创建的时间和内存开销。所以,只在真正需要跨层级通信时使用它们。

5. 依赖关系不明确

  • provideinject 会使组件之间的依赖关系变得不那么直观,因为数据传递不是通过显式的 props 进行的。这可能会给代码的维护和理解带来困难。在使用时,要做好注释和文档,明确数据的来源和用途。

除了provide和inject,Vue还有哪些跨层级组件通信的方式?

在 Vue 里,除了 provideinject,还有几种常见的跨层级组件通信方式,下面为你详细介绍:

1. 事件总线(Event Bus)

事件总线是一个简单的对象,它可以在组件间传递事件。其工作原理是,在组件中触发事件,然后在其他组件里监听这个事件。

// 创建事件总线
const eventBus = new Vue();// 发送事件的组件
export default {methods: {sendMessage() {// 触发事件并传递数据eventBus.$emit('messageSent', '这是一条消息');}}
};// 接收事件的组件
export default {created() {// 监听事件eventBus.$on('messageSent', (message) => {console.log('接收到消息:', message);});},beforeDestroy() {// 组件销毁前取消监听,避免内存泄漏eventBus.$off('messageSent');}
};

适用场景:适用于不太复杂的小型项目,能够方便地实现组件间通信。
缺点:在大型项目中,事件总线可能会使代码的可维护性变差,因为事件的触发和监听位置分散,难以追踪。

2. Vuex

Vuex 是专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

// 定义 store
import Vue from 'vue';
import Vuex from 'vuex';Vue.use(Vuex);const store = new Vuex.Store({state: {message: '初始消息'},mutations: {updateMessage(state, newMessage) {state.message = newMessage;}},actions: {changeMessage({ commit }, newMessage) {commit('updateMessage', newMessage);}}
});// 发送数据的组件
export default {methods: {sendMessage() {this.$store.dispatch('changeMessage', '新消息');}}
};// 接收数据的组件
export default {computed: {message() {return this.$store.state.message;}}
};

适用场景:适用于中大型项目,特别是需要多个组件共享状态的情况,如用户登录状态、购物车信息等。
优点:状态集中管理,便于调试和维护,能清晰地追踪状态的变化。
缺点:代码量相对较多,对于小型项目来说可能过于复杂。

3. 自定义事件和 $parent$children$refs

  • $parent$children:可以直接访问父组件和子组件的实例,从而调用它们的方法和访问数据。不过,这种方式耦合度较高,不建议在复杂的组件嵌套中使用。
// 子组件
export default {methods: {callParentMethod() {// 调用父组件的方法this.$parent.parentMethod();}}
};// 父组件
export default {methods: {parentMethod() {console.log('父组件方法被调用');}}
};
  • $refs:在父组件中可以通过 $refs 来引用子组件实例,从而调用子组件的方法和访问数据。
<template><div><!-- 给子组件添加 ref --><ChildComponent ref="childRef" /><button @click="callChildMethod">调用子组件方法</button></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},methods: {callChildMethod() {// 通过 $refs 调用子组件的方法this.$refs.childRef.childMethod();}}
};
</script>

适用场景$parent$children 适用于简单的组件嵌套场景;$refs 适用于父组件需要直接操作子组件的情况。
缺点:会增加组件间的耦合度,降低代码的可维护性。

4. Vue Router 的路由参数和路由守卫

  • 路由参数:可以在路由跳转时传递参数,然后在目标组件中获取这些参数。
// 路由配置
const routes = [{path: '/detail/:id',component: DetailComponent}
];// 跳转组件
export default {methods: {goToDetail() {// 携带参数跳转this.$router.push({ path: '/detail/1' });}}
};// 目标组件
export default {computed: {id() {return this.$route.params.id;}}
};
  • 路由守卫:可以在路由跳转前后执行一些逻辑,如验证用户登录状态等。
const router = new VueRouter({routes
});// 全局前置守卫
router.beforeEach((to, from, next) => {if (to.meta.requiresAuth && !isAuthenticated()) {next('/login');} else {next();}
});

适用场景:适用于页面间的参数传递和路由控制,如商品详情页的 ID 传递、用户登录验证等。

provide和inject的原理及应用场景
在 Vue 里,provideinject 就像是一对好搭档,能让数据在组件之间轻松传递,哪怕组件嵌套得很深也不怕。接下来,我会详细讲讲它们的原理、应用场景,并且给代码加上注释。

原理

provideinject 是用来在组件树中进行跨级数据传递的。简单来说,父组件通过 provide 提供数据,就像是把数据放在一个公共的“仓库”里;而子组件(可以是隔了很多层的子孙组件)通过 inject 来从这个“仓库”里取数据。

应用场景

  • 多层嵌套组件的数据传递:当你的组件嵌套层次很深,而你又不想一层一层地通过 props 传递数据时,provideinject 就派上用场了。
  • 插件和组件库的开发:在开发插件或者组件库时,可能需要在多个组件中共享一些配置或者状态,这时也可以使用 provideinject

代码示例

以下是一个简单的示例,展示了如何使用 provideinject

<template><!-- 根组件模板 --><div><h1>根组件</h1><!-- 渲染子组件 --><ChildComponent /></div>
</template><script>
import { provide, ref } from 'vue';
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},setup() {// 创建一个响应式数据const message = ref('这是从根组件传递下来的消息');// 通过 provide 提供数据,键为 'message',值为 messageprovide('message', message);return {};}
}
</script>
<template><!-- 子组件模板 --><div><h2>子组件</h2><!-- 渲染从根组件传递下来的消息 --><p>{{ message }}</p><!-- 渲染孙子组件 --><GrandChildComponent /></div>
</template><script>
import { inject } from 'vue';
import GrandChildComponent from './GrandChildComponent.vue';export default {components: {GrandChildComponent},setup() {// 通过 inject 注入数据,键为 'message'const message = inject('message');return {message};}
}
</script>
<template><!-- 孙子组件模板 --><div><h3>孙子组件</h3><!-- 渲染从根组件传递下来的消息 --><p>{{ message }}</p></div>
</template><script>
import { inject } from 'vue';export default {setup() {// 通过 inject 注入数据,键为 'message'const message = inject('message');return {message};}
}
</script>

代码解释

  • 根组件:使用 provide 提供了一个名为 message 的数据,这个数据是响应式的。
  • 子组件和孙子组件:使用 inject 注入了名为 message 的数据,并在模板中渲染出来。

这样,即使组件嵌套得很深,也能轻松获取到根组件提供的数据。

注意事项

  • provideinject 默认是非响应式的,如果你需要响应式的数据,需要传递响应式对象(如 refreactive 创建的对象)。
  • provideinject 是单向数据流,即数据只能从父组件流向子组件,不能反向传递。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/42024.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在Windows上下载并配置GO语言环境变量

本章教程,主要介绍如何在Windows操作系统上,下载并配置GO语言环境变量。 Go(又称为Golang)是一种开源的编程语言,由Google开发,于2009年首次公开发布。它旨在提供简洁、高效、可靠的软件开发解决方案。Golang是一种静态强类型、编译型语言,Golang具有很强的表达能力,得…

【Linux网络(五)】传输层协议

目录 1、UDP协议 1.1、UDP报头 2、TCP协议 2.1、tcp协议段格式 2.2、TCP三次握手的过程 2.3、TCP四次挥手的过程 2.4、流量控制 2.5、滑动窗口 2.6、延迟应答 2.7、拥塞控制 2.8、面向字节流 2.9、数据粘包 2.10、TCP连接异常问题 1、UDP协议 学习目标&#xff1a…

第十二:josn 传递参数 shouldBindJSON 和结构体的 db字段

链接&#xff1a; Golang教程三&#xff08;结构体、自定义数据类型&#xff0c;接口&#xff09;_golang 自定义数据类型-CSDN博客 结构体指向 json 和数据库的 db type User struct { ID int json:"id" db:"user_id" Name string json:…

Retinexformer:基于 Retinex 的单阶段 Transformer 低光照图像增强方法

开头发点牢骚&#xff1a;本来做的好好都都要中期了&#xff0c;导师怎么突然给我换题目啊。真是绷不住了......又要从头开始学了&#xff0c;唉&#xff01; 原论文链接&#xff1a;Retinexformer: One-stage Retinex-based Transformer for Low-light Image Enhancement 低光…

游戏引擎学习第182天

回顾和今天的计划 昨天的进展令人惊喜&#xff0c;原本的调试系统已经被一个新的系统完全替换&#xff0c;新系统不仅能完成原有的所有功能&#xff0c;还能捕获完整的调试信息&#xff0c;包括时间戳等关键数据。这次的替换非常顺利&#xff0c;效果很好。 今天的重点是在此基…

关于我对接了deepseek之后部署到本地将数据存储到mysql的过程

写在前面 今天写一下使用nodejs作为服务端&#xff0c;vue作为客户端&#xff0c;mysql的数据库&#xff0c;对接deepseek的全过程&#xff0c;要实现一个很简单的效果就是&#xff0c;可以自由的询问&#xff0c;然后可以将询问的过程存储到mysql的数据库中。 文档对接 deeps…

Git 提示 “LF will be replaced by CRLF“ 的原因及解决方案

遇到的问题: warning: in the working copy of build/build.js, LF will be replaced by CRLF the next time Git touches it warning: in the working copy of build/check-versions.js, LF will be replaced by CRLF the next time Git touches it warning: in the worki…

Axure设计之中继器表格——拖动列调整位置教程(中继器)

一、原理介绍 实现表格列的拖动排序&#xff0c;主要依赖Axure的动态面板和中继器两大核心功能&#xff1a; 动态面板交互控制 将表格的列标题封装在动态面板中&#xff0c;通过拖拽事件&#xff08;开始、移动、结束&#xff09;捕捉用户操作 在拖拽过程中实时计算鼠标位置&…

IDEA工具使用之启动项目失败且无日志打印

IDEA工具使用之启动项目失败且无日志打印 问题描述原因分析解决方案方案一&#xff1a;使用类路径缩短方案&#xff08;推荐&#xff09;方案二&#xff1a;修改启动配置 总结 问题描述 概述 新拉取的项目&#xff0c;基于IDEA本地调试启动失败&#xff0c;控制台也没有跳转打…

GC overhead limit exceeded---Java 虚拟机 (JVM) 在进行垃圾回收内存量非常少解决

背景&#xff1a; 我正在跑一个数据处理较为复杂的程序。然后调试了很多遍&#xff0c;出现了GC问题&#xff0c;如下图bug. GC overhead limit exceeded-这个bug错误通常表示 Java 虚拟机 (JVM) 在进行垃圾回收时花费了过多的时间&#xff0c;并且回收的内存量非常少。…

SAP GUI Script for C# SAP脚本开发快速指南与默认主题问题

SAP GUI Script for C# 快速指南 SAP 脚本的快速使用与设置. 解决使用SAP脚本执行后,默认打开的SAP是经典主题的问题 1. 解决默认主题问题 如果您使用的是SAP GUI 740,并遇到无法打开对话框的问题,请先将主题设置为经典主题(Classic Theme),应用更改后重新打开SAP GUI …

测试用例`

1.什么是测试用例 测试⽤例&#xff08;Test Case&#xff09;是为了实施测试⽽向被测试的系统提供的⼀组集合&#xff0c;这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素. 2.测试用例的万能公式(重点) 设计测试⽤例的万能公式&#xff1a; 功能测试界…

【深度学习】【目标检测】【OnnxRuntime】【C++】YOLOV5模型部署

【深度学习】【目标检测】【OnnxRuntime】【C】YOLOV5模型部署 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【目标检测】【OnnxRuntime】【C】YOLOV5模型部署前言Windows平台搭建依赖环境模型转换--pytorch转onnxONNXRuntime推…

Qt:QWebEngineView显示网页失败

今天在新电脑搭建qt开发环境&#xff0c;在运行程序时发现通过QWebEngineView显示的html失败&#xff0c;同样的代码在旧电脑上没有这个问题 分析过程 &#xff08;1&#xff09;qt出现如下信息提示 [21296:12076:0325/161831.084:ERROR:platform_handle_in_transit.cc(34)] …

第十六届蓝桥杯模拟二(串口通信)

由硬件框图可以知道我们要配置LED 和按键 一.LED 先配置LED的八个引脚为GPIO_OutPut,锁存器PD2也是,然后都设置为起始高电平,生成代码时还要去解决引脚冲突问题 二.按键 按键配置,由原理图按键所对引脚要GPIO_Input 生成代码,在文件夹中添加code文件夹,code中添加fun.…

uni-app页面怎么设计更美观

顶部 页面最顶部要获取到手机设备状态栏的高度&#xff0c;避免与状态栏重叠或者被状态栏挡住 // 这是最顶部的父级容器 <view :style"{ paddingTop: ${statusBarHeight extraPadding}px }">.... </view> export default {data() {return {statusBarH…

uniapp超简单ios截屏和上传app store构建版本方法

​ 假如使用windows开发ios的应用&#xff0c;上架的时候&#xff0c;你会发现&#xff0c;上架需要ios应用多种尺寸的ios设备的截图&#xff0c;和需要xcode等工具将打包好的ipa文件上传到app store的构建版本。 大部分情况下&#xff0c;我们的公司都没有这么多款ios设备来…

搜广推校招面经六十

soul推荐算法 一、word2vec原理 参考一篇文章入门Word2Vec 二、word2vec正负采样怎么做的、word2vec采用的loss和原理 见【搜广推校招面经四、搜广推校招面经五十二、搜广推校招面经五十七】 不太理解为啥问这么多word2vec&#xff0c;索性直接整理一遍。 三、多路召回融合…

R语言——循环

参考资料&#xff1a;学习R 在R中有三种循环&#xff1a;repeat、while和for。虽然向量化意味着我们可能并不需要大量使用它们&#xff0c;但在需要重复执行代码时&#xff0c;它们是非常有用的。 1、重复循环 R中最容易掌握的循环是repeat。它所做的事情就是反复地执行代码&a…

nginx代理前端请求

一&#xff0c;项目配置 我在 ip 为 192.168.31.177 的机器上使用 vue3 开发前端项目&#xff0c;项目中使用 axios 调用后端接口。 这是 axios 的配置&#xff1a; import axios from axios;const request axios.create({baseURL: http://192.168.31.177:8001,// 设置请求…