IOC技术在前端项目中的应用
背景
前端发展至今已经过去30余年,前端应用领域在不断壮大的过程中,也变得越来越复杂,随着代码行数和项目需求的增加,内部模块间的依赖可能也会随之越来越复杂,模块间的 低复用性 导致应用 难以维护,不过我们可以借助计算机领域的一些优秀的编程理念来一定程度上解决这些问题,接下来要讲述的 IoC 就是其中之一。
什么是IOC
其实学过java的就一定会知道java中有一个非常著名的框架叫做springboot,它就是将AOP和IOC等概念运用到了极致的代表作,那么具体IOC是做什么的呢,我们可以看下下面一段描述。
IoC 的全称叫做 Inversion of Control,可翻译为为「控制反转」或「依赖倒置」,它主要包含了三个准则:
- 高层次的模块不应该依赖于低层次的模块,它们都应该依赖于抽象
- 抽象不应该依赖于具体实现,具体实现应该依赖于抽象
- 面向接口编程 而不要面向实现编程
假设我们有一个类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中配置experimentalDecorators为true开启支持。
首先我们先看下我们需要实现的最后效果
@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技术在前端项目中的应用的更多相关文章
- 前端项目中使用jsencrypt进行字段加密
前端项目中使用jsencrypt进行字段加密. 使用步骤:①获取公钥②实例化对象③设置公钥④将所需数据进行加密然后返回. 进行一个简单的封装如下 /** * npm install jsencrypt ...
- 在Vue&Element前端项目中,使用FastReport + pdf.js生成并展示自定义报表
在我的<FastReport报表随笔>介绍过各种FastReport的报表设计和使用,FastReport报表可以弹性的独立设计格式,并可以在Asp.net网站上.Winform端上使用, ...
- 在Vue&Element前端项目中,对于字典列表的显示处理
在很多项目开发中,我们为了使用方便,一般都会封装一些自定义组件来简化界面的显示处理,例如参照字典的下拉列表显示,是我们项目中经常用到的功能之一,本篇随笔介绍在Vue&Element前端项目中如 ...
- 在Vue前端项目中,附件展示的自定义组件开发
在Vue前端界面中,自定义组件很重要,也很方便,我们一般是把一些通用的界面模块进行拆分,创建自己的自定义组件,这样操作可以大大降低页面的代码量,以及提高功能模块的开发效率,本篇随笔继续介绍在Vue&a ...
- web前端项目中遇到的一些问题总结(08.23更新)
个人网站 https://iiter.cn 程序员导航站 开业啦,欢迎各位观众姥爷赏脸参观,如有意见或建议希望能够不吝赐教! 写一些最近工作中Vue项目中遇到的问题. 巴啦啦小魔仙,污卡拉,全身变,小 ...
- 如何在前端项目中引用bootstrap less?
在基于bootstrap css框架的前端项目开发中,如果有grunt build系统,那么工作流是:客制化less,在less中定义自己的 CSS,同时可以随意引用bootstrap中预定义好的cs ...
- 前端项目中gulp的使用
在公司项目开发中,有一个前端项目,我们使用gulp来生成目标文件(css,js,html文件) 进入到这个项目目录中 C:\My Project\FrontEnd\TestBuilder 然后依次运 ...
- 前后端分离,如何在前端项目中动态插入后端API基地址?(in docker)
开门见山,本文分享前后端分离,容器化前端项目时动态插入后端API基地址,这是一个很赞的实践,解决了前端项目容器化过程中受制后端调用的尴尬. 尴尬从何而来 常见的web前后端分离:前后端分开部署,前端项 ...
- 前端项目中常用es6知识总结 -- let、const及数据类型延伸
项目开发中一些常用的es6知识,主要是为以后分享小程序开发.node+koa项目开发以及vueSSR(vue服务端渲染)做个前置铺垫. 项目开发常用es6介绍 1.块级作用域 let const 2 ...
随机推荐
- [水题日常]UVA1639 糖果(Candy,ACM/ICPC Chengdu 2012)
今天来尝试了几道数学期望相关的题,这是我认为比较有趣的一道题 这次不废话啦直接开始~ 一句话题意:两个分别装有n个糖果的盒子,每次随机选一个盒子然后拿走一颗糖(选的概率分别是\(p\)和\((1-p) ...
- Flink如何做维表关联?
使用 RichAsyncFunction 加 CacheBuilder CacheBuilder.newBuilder() //最多存储10000条 .maximumSize(10000) //过期时 ...
- Spark性能调优篇三之广播方式传输数据
广播大变量,重复用到的变量 原因见 https://www.jianshu.com/p/2c297b23ebda
- pandas的学习3-设置值
import pandas as pd import numpy as np # 我们可以根据自己的需求, 用 pandas 进行更改数据里面的值, 或者加上一些空的,或者有数值的列. # 首先建立了 ...
- linux里用户权限:~$,/$,~#,/#的区别与含义
$表明是非root用户登录,#表示是root用户登录,它们是终端shell的命令提示符几种常用终端的命令提示符 BASH: root账户: # ,非root账户: $KSH: root账户: # ...
- iOS UIcollectionView 实现卡牌翻转效果
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...
- springboot项目报错Exception getting JDBC Driver: com.mysql.cj.jdbc.Driver
将驱动换成 <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysq ...
- String Boot有哪些优点
a.减少开发,测试时间和努力. b.使用 JavaConfig 有助于避免使用 XML.c.避免大量的 Maven 导入和各种版本冲突. d.通过提供默认值快速开始开发.没有单独的 Web 服务器需要 ...
- CI持续集成理论知识
(1)什么是CI What is CI? CI就是持续集成,持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成.每次集成都通过 ...
- Docker技术