背景

前端发展至今已经过去30余年,前端应用领域在不断壮大的过程中,也变得越来越复杂,随着代码行数和项目需求的增加,内部模块间的依赖可能也会随之越来越复杂,模块间的 低复用性 导致应用 难以维护,不过我们可以借助计算机领域的一些优秀的编程理念来一定程度上解决这些问题,接下来要讲述的 IoC 就是其中之一。

什么是IOC

其实学过java的就一定会知道java中有一个非常著名的框架叫做springboot,它就是将AOP和IOC等概念运用到了极致的代表作,那么具体IOC是做什么的呢,我们可以看下下面一段描述。

IoC 的全称叫做 Inversion of Control,可翻译为为「控制反转」或「依赖倒置」,它主要包含了三个准则:

  1. 高层次的模块不应该依赖于低层次的模块,它们都应该依赖于抽象
  2. 抽象不应该依赖于具体实现,具体实现应该依赖于抽象
  3. 面向接口编程 而不要面向实现编程

假设我们有一个类Human,要实例一个Human,我们需要实例一个类Clothes。而实例化衣服Clothes,我们又需要实例化布Cloth,实例化纽扣等等。

当需求达到一定复杂的程度时,我们不能为了一个人穿衣服去从布从纽扣开始从头实现,最好能把所有的需求放到一个工厂或者是仓库,我们需要什么直接从工厂的仓库里面直接拿。

这个时候就需要依赖注入了,我们实现一个IOC容器(仓库),然后需要衣服就从仓库里面直接拿实例好的衣服给人作为属性穿上去。

这也就大大减少了我们编码的成本。

如何实现一个IOC

其实实现IOC的思路很简单,或者说这是一个很轻的东西,任何人只要知道原理都能去实现它。首先我们重复下刚刚所描述的ioc的概念,在正常情况下我们需要Human,Clothes类的时候都只能一个一个新建。

export class Human {}

export class Clothes {}

function test() {
const human = new Human();
const clothes = new Clothes();
}

我们不难看出少量的对象需要新建的时候这么做确实没啥问题,但是如果在一个庞大系统中存在上百上千个对象,我们在不同业务场景又需要load不同的对象,同时我们还需要控制对象销毁避免GC。这样来说我们想要处理好前端对象我们得做很多工作,这样我们就引出了接下来我们需要做的工作如何去管理对象。

第一步:实现一个容器

容器其实是一个高大上的概念,其实简单来说就是个Map对象之类的东西,用于存放现有的对象。下面是我具体实现的一个小demo,主要是存放的容器类。为了保证容器唯一,所以我将其设计成了单例模式。

export class SimpleContainer {
private containerMap = new Map<string | symbol, any>();
private static _instance: SimpleContainer; public set(id: string | symbol, value: any): void {
this.containerMap.set(id, value);
} public get<T extends any>(id: string | symbol): T {
return this.containerMap.get(id) as T;
} public has(id: string | symbol): Boolean{
return this.containerMap.has(id);
} public remove(id: string | symbol): void {
if (this.containerMap.has(id)) {
this.containerMap.delete(id);
}
} public static getInstance(): SimpleContainer {
if(!this._instance) {
this._instance = new SimpleContainer();
}
return this._instance;
} public get container(): SimpleContainer {
return SimpleContainer._instance;
}
}

第二步:用好装饰器

随着TypeScript和ES6里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。 Javascript里的装饰器目前处在 建议征集的第二阶段,但在TypeScript里已做为一项实验性特性予以支持。

注意  装饰器是一项实验性特性,在未来的版本中可能会发生改变。

如果需要使用装饰器,我们得在tsconfig.json中配置experimentalDecoratorstrue开启支持。

首先我们先看下我们需要实现的最后效果

@Service('human')
export class Human {} @Service('clothes')
export class Clothes {} export class Test { @Inject()
private human!: Human; }

我们需要通过Service注入需要实例化的类,然后再通过Inject在外面需要的对象中注入进去,这就是装饰器在IOC中所发挥的作用。

那么Service是如何实现的呢?

export function Service(idOrSingleton?: string | boolean, singleton?: boolean): Function {
return (target: ConstructableFunction) => {
let id;
let singleton;
const container = SimpleContainer.getInstance();
// 代码逻辑复杂有所删减
container.set(id, singleInstance || new target());
};
};

我们所有的实例初始化都在Service中实现也就是这么一个句话,container.set(id, singleInstance || new target());。

export function Inject(value?: string): PropertyDecorator {
return (target: any, propertyKey: string | symbol) => {
const id = value || propertyKey;
const container = SimpleContainer.getInstance();
const _dependency = container.get(id) ? container.get(id) : null;
if (_dependency) {
target[propertyKey] = _dependency;
}
return target;
};
}

通过Inject来实现对象的实例话和返还,所利用的特性也是PropertyDecorator所支持的能够对参数赋值的能力。识别到对应装饰器的对象的时候,我们通过属性装饰器来进行赋值和初始化。

这里需要补充一下装饰器的相关知识。

1.装饰器对类的行为改变是在编译时,而非在运行时。

2.装饰器运行顺序,并非按照类,属性,方法来进行的,我们在使用的时候需要注意,我这里的顺序是:属性->类->方法

第三步:使用容器

我们又回到了第二步的最初,当我们实现了Inject和Service装饰器之后我们就可以快乐的初始化了。

@Inject()
private human!: Human;

通过如上操作之后我们就可以使用该对象的内容了。

扩展和展望

回到我们实现IOC的初衷,我们希望通过某种技术来管理我们繁乱的对象和代码,所以我们才做了这么一个容器,当然现在这个容器还十分简陋,依然还有很多可以扩展的空间,比如说:关于对象的生命周期的控制,如何更加友好的使用容器中的对象。

最后

一个小广告,欢迎使用基于上述代码所开发的ioc包,目前还能简陋,不过笔者会迅速强化和迭代它。

easy-ts-di:https://github.com/guanjiangtao/easy-ts-di

欢迎大佬们可以提供意见,钢筋走开~~~~

IOC技术在前端项目中的应用的更多相关文章

  1. 前端项目中使用jsencrypt进行字段加密

    前端项目中使用jsencrypt进行字段加密. 使用步骤:①获取公钥②实例化对象③设置公钥④将所需数据进行加密然后返回. 进行一个简单的封装如下 /** * npm install jsencrypt ...

  2. 在Vue&Element前端项目中,使用FastReport + pdf.js生成并展示自定义报表

    在我的<FastReport报表随笔>介绍过各种FastReport的报表设计和使用,FastReport报表可以弹性的独立设计格式,并可以在Asp.net网站上.Winform端上使用, ...

  3. 在Vue&Element前端项目中,对于字典列表的显示处理

    在很多项目开发中,我们为了使用方便,一般都会封装一些自定义组件来简化界面的显示处理,例如参照字典的下拉列表显示,是我们项目中经常用到的功能之一,本篇随笔介绍在Vue&Element前端项目中如 ...

  4. 在Vue前端项目中,附件展示的自定义组件开发

    在Vue前端界面中,自定义组件很重要,也很方便,我们一般是把一些通用的界面模块进行拆分,创建自己的自定义组件,这样操作可以大大降低页面的代码量,以及提高功能模块的开发效率,本篇随笔继续介绍在Vue&a ...

  5. web前端项目中遇到的一些问题总结(08.23更新)

    个人网站 https://iiter.cn 程序员导航站 开业啦,欢迎各位观众姥爷赏脸参观,如有意见或建议希望能够不吝赐教! 写一些最近工作中Vue项目中遇到的问题. 巴啦啦小魔仙,污卡拉,全身变,小 ...

  6. 如何在前端项目中引用bootstrap less?

    在基于bootstrap css框架的前端项目开发中,如果有grunt build系统,那么工作流是:客制化less,在less中定义自己的 CSS,同时可以随意引用bootstrap中预定义好的cs ...

  7. 前端项目中gulp的使用

    在公司项目开发中,有一个前端项目,我们使用gulp来生成目标文件(css,js,html文件) 进入到这个项目目录中  C:\My Project\FrontEnd\TestBuilder 然后依次运 ...

  8. 前后端分离,如何在前端项目中动态插入后端API基地址?(in docker)

    开门见山,本文分享前后端分离,容器化前端项目时动态插入后端API基地址,这是一个很赞的实践,解决了前端项目容器化过程中受制后端调用的尴尬. 尴尬从何而来 常见的web前后端分离:前后端分开部署,前端项 ...

  9. 前端项目中常用es6知识总结 -- let、const及数据类型延伸

    项目开发中一些常用的es6知识,主要是为以后分享小程序开发.node+koa项目开发以及vueSSR(vue服务端渲染)做个前置铺垫. 项目开发常用es6介绍 1.块级作用域 let const  2 ...

随机推荐

  1. 多任务-python实现-生成器相关(2.1.13)

    @ 目录 1.概念 2.创建方法 3.通过send方式来启动 1.概念 通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占 ...

  2. SpringBoot事件监听机制及观察者模式/发布订阅模式

    目录 本篇要点 什么是观察者模式? 发布订阅模式是什么? Spring事件监听机制概述 SpringBoot事件监听 定义注册事件 注解方式 @EventListener定义监听器 实现Applica ...

  3. GitHub 上的大佬们打完招呼,会聊些什么?

    你好 GitHub!每一位开源爱好者的好朋友「HelloGitHub」 大家好,今儿 HG 有幸邀请到:Lanking 一位亚马逊 AI 软件工程师.开源爱好者和贡献者.他是亚马逊开源的 Java 深 ...

  4. 微服务之间如何共享DTO?

    1. 概述 近些年来,微服务变得越来越流行.微服务基本特征是模块化.独立.易于扩展的.它们之间需要协同工作并交换数据.为了实现这一点,我们创建了名为 DTO 的共享数据传输对象.在本文中,我们将介绍在 ...

  5. JXL封装不能使用static关键字问题

    最近要做一个Excel导出的功能,由于文件不大,涉及到了很多Excel表格样式和公式计算,我采用了JXL的方式导出.由于逻辑大多是金额,所以我在封装JXL的时候写了两个静态final变量,代码如下: ...

  6. ADF 第八篇:传递参数(Pipeline的Parameter和Variable,Activity的output)和应用表达式

    Azure Data Factory传递参数的方式主要有两种,通过Pipeline的Parameter和Variable来传递参数,通过Activity的输出来传递参数.要在Activity中引用Pa ...

  7. Android插件换肤 一.实现原理

    学习缺的不是时间,而是耐心 目的 1.搞懂系统获取资源文件到在加载布局的整个流程是自己实现换肤功能的理论基础 2.提高分析源码.追踪源码的能力 要点 1.XmlResourceParser (通过这个 ...

  8. mysql数据安全之利用二进制日志mysqlbinlog恢复数据

    mysql数据安全之利用二进制日志mysqlbinlog恢复数据 简介:如何利用二进制日志来恢复数据 查看二进制日志文件的内容报错: [root@xdclass-public log_bin]# my ...

  9. Object[] cannot be converted to String[]

    原因: 你应该是想把List数组转 String数组吧! 然后想当然的调用list.toArray()方法. 结果 该方法返回的是Object[]数组,导致类型不匹配! 解决办法: 还在乖乖的用循环吧 ...

  10. eclipse中把spring源码关联至当前工程

    1.下载并导入spring的相关jar包下载对应版本的spring 2.在当前工程中,选择Referenced Libraries,展开后,选择相应的jar包,右击并选择Properties 3. 在 ...