理解依赖注入(DI – Dependency Injection)

news2024/12/24 13:09:25

文章目录

  • 依赖注入
  • 1.provide(提供)
          • 1.1 在选项式 API 中,可通过provide选项为后代提供数据
          • 1.2 如果想访问到组件的实例this,provide必须采用函数的方式(不能用箭头函数),为保证注入方和供给方之间的响应性链接,必须借助组合式 API 中的computed()函数提供计算属性,还可以提供修改响应式数据的函数(响应式数据的修改,尽量放在同一个组件中,为了好维护)
  • 2.inject(注入)
          • 2.1 在选项式 API 中,可通过inject选项来声明需要注入祖先组件提供的数据,他们可以在JS中直接通过this来访问,在视图模板中也可直接访问
          • 2.1 inject采用对象的形式来注入祖先组件提供的数据有哪些好处?
  • 3. 例子
  • 总结


依赖注入

如果有一个深层的子组件需要一个较远的祖先组件中的部分数据,如果实现呢?

  1. 可使用props沿着组件链逐级传递下去,如图 1
  2. 我们可在祖先组件使用provide提供数据,后代组件使用inject注入数据,如图 2

图1:
在这里插入图片描述

图2:
在这里插入图片描述

1.provide(提供)

在应用层如何提供:
在应用层方面可通过app.provide()为后代提供数据

//应用层提供数据
import { createApp } from 'vue'
const app = createApp({ })
app.provide('message', 'hello!') // message 注入名, 'hello'

在组件中如何提供:

1.1 在选项式 API 中,可通过provide选项为后代提供数据
//Provide 选项提供数据【选项式】
export default {
    // 为后代组件提供数据选项
    provide: { title: '你好世界!' } // message:注入名,'hello':值
}

在组合式 API script setup 中,可通过provide()函数来为后代组件提供数据

//使用 provide 函数提供数据
<script setup>
import { ref, provide } from 'vue'

const message = 'hello'
const title = ref('你好世界')
const subtitle = ref('分享经验')

function changeSubtitle(sub) {
    this.subtitle = sub
}

provide('message', message) // 提供固定数据
provide('title', title) // 提供响应式数据
provide('subtitle', subtitle) // 提供响应式数据
provide('changeSubtitle', changeSubtitle) // 为后代组件提供修改响应式数据 subtitle 的函数
</script>
1.2 如果想访问到组件的实例this,provide必须采用函数的方式(不能用箭头函数),为保证注入方和供给方之间的响应性链接,必须借助组合式 API 中的computed()函数提供计算属性,还可以提供修改响应式数据的函数(响应式数据的修改,尽量放在同一个组件中,为了好维护)
//Provide 函数选项提供数据【选项式】
export default {
    data: () => ({
        title: '你好世界',
        subtitle: '经验'
    }),
    methods: {
        changeSubtitle(sub) {
            this.subtitle = sub
        }
    },
    // 使用函数的形式,可以访问到组件的实例 `this`
    provide() {
        return {
            // 传递数据的值为数据源 title,此方式注入方和供给方之间没有响应性链接
            title: this.title,
            // 传递数据的值为数据源 subtitle,此方式注入方和供给方之间具有响应性链接
            subtitle: computed(() => this.subtitle),
            // 为后代组件提供修改响应式数据 subtitle 的函数
            changeSubtitle: this.changeSubtitle
        }
	}
}

注意:provide选项中通过computed函数提供的响应式的数据,需要设置app.config.unwrapInjectedRef = true以保证注入会自动解包这个计算属性。这将会在 Vue 3.3 后成为一个默认行为,而我们暂时在此告知此项配置以避免后续升级对代码的破坏性。在 3.3 后就不需要这样做了。

2.inject(注入)

2.1 在选项式 API 中,可通过inject选项来声明需要注入祖先组件提供的数据,他们可以在JS中直接通过this来访问,在视图模板中也可直接访问

选项式

export default {
    // 注入祖先组件提供的数据
    inject: ['message', 'title', 'subtitle', 'changeSubtitle'] 
}

组合式

在这里插入代码片

**在组合式 API 中,使用inject()函数的返回值来注入祖先组件提供的数据

  1. 如果提供数据的值是一个ref,注入进来的会是该ref对象,和提供方保持响应式连接
  2. 如果注入的数据并没有在祖先组件中提供,则会抛出警告,可在provide()函数的第二个参数设置默认值来解决
  3. 他们可以在JS和视图模板中直接访问**
<script setup>
import { inject } from 'vue'

const c_message = inject('message')
const title = inject('title')
const c_subtitle = inject('subtitle')
const c_changeSub = inject('changeSubtitle')
// 祖先组件并未提供 content,则会报警告,设置默认值来解决
const c_content = inject('content',  '暂时还未有内容') 
</script>
2.1 inject采用对象的形式来注入祖先组件提供的数据有哪些好处?

a. 可用本地属性名注入祖先组件提供的数据(如相同时,from选项可省略)
b. 如果注入的数据并没有在祖先组件中提供,则会抛出警告,可采用defalut选项设置默认值来解决

inject 对象形式【选项式】

export default {
    // 注入祖先组件提供的数据
    inject: {
        c_message: { 
            from: 'message', // 注入的哪个数据
        },
        // 本地属性名和需要注入的数据名一致时,from 可省略
        title, // 普通数据
        c_subtitle: {from: 'subtitle'}, // 响应式数据
        c_changeSub: {from: 'changeSubtitle'}, // 修改响应式数据 subtitle 的函数
        c_content: {
            from: 'content', // 祖先组件并未提供 content,则会报警告
            default: '暂时还未有内容' // 设置默认值(可为函数等),解决警告问题
        } 
    }
}

3. 例子

main.js

import { createApp } from 'vue'
import App from './App.vue'

let app = createApp(App)

// 为所有的组件提供数据
app.provide('message', '登陆成功')

// provide 选项中通过 computed 函数提供的响应式的数据,需要设置此项以保证注入会自动解包这个计算属性。
app.config.unwrapInjectedRef = true

app.mount('#app')

app.vue 选项式

<script>
import { computed } from 'vue';
import FooterVue from './components/Footer.vue'
export default {
    components: { FooterVue },
    data: () => ({
        title: '博客',
        subtitle: '百万博主分享经验'
    }),
    methods: {
        changeSubtitle(sub) {
            this.subtitle = sub
        }
    },
    // provide: { title: this.title }, // 对象方式访问不到 this
    // 如果想访问组件实例 this,需要采用函数方式
    provide() {
        return {
            title: this.title, // 和注入方不具备响应式连接
            subtitle: computed(() => this.subtitle), // 借助组合式 API 中的 computed 函数,提供和注入方具备响应式连接的数据
            changeSubtitle: this.changeSubtitle // 提供修改响应式 subtitle 的函数
        }
    }
}
</script>

<template>
    <div class="area" style="background-color: red">
        <h3>这是 APP 组件</h3>
        副标题:<input type="text" v-model="subtitle">
        <FooterVue />
    </div>
</template>

<style>
.area {
    padding: 15px;
}
</style>

app.vue 组合式

<script setup>
import { provide, ref } from 'vue';
import FooterVue from './components/Footer.vue'

let title = '博客'
let subtitle = ref('百万博主分享经验')
function changeSubtitle(sub){
    subtitle.value = sub
}

// 为后代组件提供数据
provide('title', title) // 提供普通的数据
provide('subtitle', subtitle) // 提供响应式的数据,自动和注入方保持响应式的连接
provide('changeSubtitle', changeSubtitle) // 提供修改响应式的数据 subtitle 的函数
</script>

<template>
    <div class="area" style="background-color: red">
        <h3>这是 APP 组件</h3>
        副标题:<input type="text" v-model="subtitle">
        <FooterVue />
    </div>
</template>

<style>
.area {
    padding: 15px;
}
</style>

Footer.vue选项式

<script>
import DeepChildVue from './DeepChild.vue';
export default {
    components: { DeepChildVue }
}
</script>

<template>
    <div class="ar ea" style="background-color: yellow">
        <h3>这是 Footer 组件</h3>
        <DeepChildVue />
    </div>
</template>

Footer.vue组合式

<script setup>
import DeepChildVue from './DeepChild.vue';
</script>

<template>
    <div class="area" style="background-color: yellow">
        <h3>这是 Footer 组件</h3>
        <DeepChildVue />
    </div>
</template>

DeepChild.vue选项式

<script>
export default {
    // inject: ['message', 'title', 'subtitle', 'changeSubtitle'],
    // 注入祖先组件提供的数据
    inject: {
        d_message: { from: 'message' }, // 应用层数据
        title: {}, // 普通数据,如果注入属性名和本地名一致,则 from 可省略
        d_subtitle: { from: 'subtitle' }, // 响应式数据
        d_changeSub: { from: 'changeSubtitle' }, // 函数
        d_content: { from: 'content' }, // 祖先组件并未提供数据,则会抛出警告
        d_action: { from: 'action' , default: '关注博客'} // 祖先组件并未提供数据,则会抛出警告,可设置默认值,警告则取消
    },
    methods: {
        showInjectData() {
            console.log('应用层提供的数据 message 的值:' + this.d_message);
            console.log('APP 组件提供的 title 的值:' + this.title);
            console.log('APP 组件提供的 subtitle 的值:');
            console.log(this.d_subtitle);
            console.log('获取祖先组件提供 content 的数据(并没有):' + this.d_content);
            console.log('获取祖先组件提供 action 的数据(并没有,但是设置默认值):' + this.d_action);
            this.d_changeSub('EDF')
        }
    }
}
</script>


<template>
    <div class="area" style="background-color: pink">
        <h3>这是 DeepChild 组件</h3>
        <ul>
            <li>应用层提供的数据 message 的值:{{ d_message }}</li>
            <li>APP 组件提供的 title 的值:{{ title }}</li>
            <li>APP 组件提供的 subtitle 的值:{{ d_subtitle }}</li>
            <li>获取祖先组件提供 content 的数据(并没有):{{ d_content }}</li>
            <li>获取祖先组件提供 action 的数据(并没有,但是设置默认值):{{ d_action }}</li>
        </ul>
        <button @click="d_changeSub('ABC')">修改APP组件中 subtitle 的值</button>
        <button @click="showInjectData">在 JS 中访问注入的数据</button>
    </div>
</template>

DeepChild.vue组合式

<script setup>
import { inject } from 'vue';

// 注入祖先组件提供的数据
let d_message = inject('message') // 应用层提供的数据
let d_title = inject('title') // 普通数据
let d_subtitle = inject('subtitle') // ref 响应式数据,则 d_subtitle 也是 ref 对象
let d_changeSub = inject('changeSubtitle') // 函数
let d_content = inject('content') // 祖先组件并未提供,则会抛出警告
let d_action = inject('action', '关注博客') // 祖先组件并未提供,则会抛出警告

function showInjectData() {
    console.log('应用层提供的数据 message 的值:' + d_message);
    console.log('APP 组件提供的 title 的值:' + d_title);
    console.log('APP 组件提供的 subtitle 的值:');
    console.log(d_subtitle);
    console.log('获取祖先组件提供 content 的数据(并没有):' + d_content);
    console.log('获取祖先组件提供 action 的数据(并没有,但是设置默认值):' + d_action);
    d_changeSub('EDF') 
}
</script>


<template>
    <div class="area" style="background-color: pink">
        <h3>这是 DeepChild 组件</h3>
        <ul>
            <li>应用层提供的数据 message 的值:{{ d_message }}</li>
            <li>APP 组件提供的 title 的值:{{ d_title }}</li>
            <li>APP 组件提供的 subtitle 的值:{{ d_subtitle }}</li>
            <li>获取祖先组件提供 content 的数据(并没有):{{ d_content }}</li>
            <li>获取祖先组件提供 action 的数据(并没有,但是设置默认值):{{ d_action }}</li>
        </ul>
        <button @click="d_changeSub('ABC')">修改APP组件中 subtitle 的值</button>
        <button @click="showInjectData">在 JS 中访问注入的数据</button>
    </div>
</template>

总结

依赖注入(Dependency Injection, DI)是一种设计模式,也是Spring框架的核心概念之一。其作用是去除Java类之间的依赖关系,实现松耦合,以便于开发测试。为了更好地理解DI,先了解DI要解决的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

K8s:开源安全平台 kubescape 实现 Pod 的安全合规检查/镜像漏洞扫描

写在前面 生产环境中的 k8s 集群安全不可忽略&#xff0c;即使是内网环境容器化的应用部署虽然本质上没有变化&#xff0c;始终是机器上的一个进程但是提高了安全问题的处理的复杂性分享一个开源的 k8s 集群安全合规检查/漏洞扫描 工具 kubescape博文内容涉及&#xff1a; kube…

工控机如何安装Python

钡铼技术BL302基于arm架构工控机&#xff0c;采用NXP的高性能处理器I.MX6ULL 运行速度高达800MHz&#xff0c;并配有8GFlash空间和512M RAM&#xff0c;硬件接口有2个网口、2个串口、1个USB口、1个SD卡卡槽、1个HDMI显示接口&#xff0c;可运行LINUX、Ubuntu、Debian等OS&#…

项目管理:如何做到项目信息透明?

项目管理中&#xff0c;管理者最担心的问题是什么&#xff1f;方项目进度不透明&#xff0c;项目的进展不明确&#xff0c;使用项目管理工具让项目进度一目了然。 1、项目管理必须目标明确。 明确的目标能够更好地指导接下来的一系列项目管理工作。 2、项目管理必须资源配置…

Redis缓存一致

背景介绍&#xff1a; redis是一个key-value存储系统。它支持存储的value类型相对更多&#xff0c;包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash&#xff08;哈希类型&#xff09;。这些数据类型都支持push/pop、add/remove及取交集并集和差…

2023 工业互联网平台:智慧制硅厂 Web SCADA 生产线

我国目前是全球最大的工业硅生产国、消费国和贸易国&#xff0c;且未来该产业的主要增量也将来源于我国。绿色低碳发展已成为全球大趋势和国际社会的共识&#xff0c;随着我国“双碳”目标的推进&#xff0c;光伏产业链快速发展&#xff0c;在光伏装机需求的带动下&#xff0c;…

干货分享!PCBA元器件间距的可焊性设计

“ SMT贴片加工逐步往高密度、细间距的设计发展&#xff0c;元器件的最小间距设计需考虑smt厂家的经验程度和工艺是否完善。元器件最小间距的设计除了保证smt焊盘间不易短接的安全间距外&#xff0c;还应考虑元器件的可维护性。 ” 元器件布局为什么要保证安全间距&#xff…

喜讯!华秋电子荣获第六届“高新杯”十大优秀企业奖

2月22日&#xff0c;由中科为集团与广东高科技产业商会联合举办的“第六届‘高新杯’十大优秀高新企业颁奖典礼暨2023年高新企业高质量发展春茗会”&#xff0c;在深圳隆重举行。 中科为集团自2014年举办首届“高新杯”以来&#xff0c;已主办了5届&#xff0c;共计600多家企业…

如何在CSDN上赚钱:CSDN收益渠道分析

文章目录说明哪些人&#xff0c;哪些资源赚到了钱&#xff1f;上传付费资源的策略一些经验说明 在CSDN平台上要想获得回报&#xff0c;主要的渠道&#xff1a; 付费资源付费专栏文章打赏 这三个都有门槛&#xff0c;上传付费资源需要原力等级达到Lv5&#xff0c;粉丝数500&a…

微信怎么发文字朋友圈?简单快捷的方法,只需1分钟

微信是一个非常受欢迎的聊天工具&#xff0c;也是一个交友&#xff0c;分享生活的平台。其中&#xff0c;朋友圈是最受欢迎的功能之一&#xff0c;让我们可以与好友分享自己的生活点滴&#xff0c;也能够关注他人的动态。那么微信怎么发文字朋友圈呢&#xff1f;下面跟着小编一…

tensorflow lite简介-移动设备端机器学习

TensorFlow Lite 是一组工具&#xff0c;可帮助开发者在移动设备、嵌入式设备和 loT 设备上运行模型&#xff0c;以便实现设备端机器学习。 支持多平台 支持多种平台&#xff0c;涵盖 Android 和 iOS 设备、嵌入式 Linux 和微控制器。 原理/流程 工作原理或者使用流程就是上面…

物联网WEB大屏数据可视化

最近了解WEB大屏显示。一般像嵌入式这类的&#xff0c;MQTT协议会走的多一些&#xff0c;走订阅和发布的策略&#xff0c;网上走了一圈之后&#xff0c;目前有几个实现方案。这里对比一下几个物联网协议&#xff0c;相对而言MQTT更合适物联网&#xff0c;其它几个协议不是干这个…

ChatGPT解答:安卓APP耗电量测试方案和源码,用Android代码实现

ChatGPT解答&#xff1a;安卓APP耗电量测试方案和源码&#xff0c;用Android代码实现 安卓APP耗电量测试方案和源码&#xff0c;用Android代码实现 作为一名AI助手&#xff0c;我不会编写代码&#xff0c;但我可以为您提供一些关于安卓APP耗电量测试方案和源码的信息和建议。 …

Web前端:2023年React最佳实践

大多数前端开发人员都希望React JS能成为他们的武器库&#xff0c;来创建独特的、前沿的用户界面。1.重要文件组织服务于相似功能并且被设计为使用某一特征的文件应该被保存在一起。例如&#xff0c;与单个组件相关的所有样式文件和其他文件应该位于同一位置。在这种做法的帮助…

iphone备忘录删除怎么恢复?分享苹果数据找回办法

手机备忘录上写记录&#xff0c;这是不少上班族的小习惯。因为它可以先记录紧急事务&#xff0c;然后再慢慢的解决。也可以把我们一些重要的账号密码存在备忘录里&#xff0c;方便在何时何地直接登入使用。那么如果我们不小心删除了iphone备忘录呢?碰到这种事该怎么办呢?有没…

Vue中二级菜单的实现

在不使用ElementUi等框架的情况下&#xff0c;制作一个二级菜单&#xff0c;网上搜寻很多资料&#xff0c;但部分要不只显示HTML结构&#xff0c;不显示CSS样式&#xff0c;要不就是复杂的让人无法理解。效果图&#xff1a;针对菜单做了CSS样式修饰&#xff0c;给一级二级菜单都…

基于STM32+CS创世 SD NAND(贴片SD卡)完成FATFS文件系统移植与测试(下篇)

四、移植FATFS文件系统 前面第3章&#xff0c;完成了SD NAND的驱动代码编写&#xff0c;这一章节实现FATFS文件的移植。 4.1 FATFS文件系统介绍 &#xff08;1&#xff09;介绍 FatFs 是一种完全免费开源的 FAT 文件系统模块&#xff0c;专门为小型的嵌入式系统而设计。它完…

07--组件

一、小程序组件分类微信团队为开发者提供了一系列基础组件&#xff0c;开发者可以通过组合这些基础组件进行快速开发。小程序中的组件也是非常丰富的&#xff0c;开发者可以基于组件快速搭建出漂亮的页面结构。小程序中的组件其实相当于网页中的HTML标签&#xff0c;只不过标签…

5年测试路,终于爬到了半山腰,结果碰到00后入场,我该拿什么争,我不想35岁被淘汰......

软件测试是一个付出就有回报的工作&#xff0c;可能很多人会说软件测试就是吃青春饭&#xff0c;然而其他工作又何尝不是&#xff1f;没有哪一家公司养尸位素餐之人&#xff0c;大龄员工有被辞退的&#xff0c;也有没被辞退的。干任何职业&#xff0c;抱着一劳永逸的心态&#…

关于k8s集群备份和恢复工具Velero 的一些笔记整理

写在前面 分享一个k8s集群容灾备份恢复开源工具 Velero博文内容涉及&#xff1a; Velero 的简单介绍Velero 安装下载备份恢复 Demo&#xff0c;以及容灾测试 Demo恢复失败情况分析 理解不足小伙伴帮忙指正 我所渴求的&#xff0c;無非是將心中脫穎語出的本性付諸生活&#xff0…

第九届蓝桥杯省赛 C++ A/B组 - 全球变暖

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;全球变暖 &#x1f4e3;专栏定位&#xff1a;为想参加蓝桥杯的小伙伴整理常考算法题解&#xff0c;祝大家…