ReactNative IoC 框架 InversifyJS 解读
一、简介
InversifyJS
,一个强大又轻量的控制反转容器,提供给JavaScript
和 Node.js
应用使用,使用TypeScript
编写。
InversifyJS
是一个轻量的 (4KB
) 控制反转容器 (IoC
),可用于编写 TypeScript
和 JavaScript
应用。 它使用类构造函数去定义和注入它的依赖。InversifyJS API
友好易懂, 是鼓励对 OOP
和 IoC
最佳实践的应用.
二、InversifyJS 为何而生 ?
JavaScript
现在支持面向对象编程,基于类的继承。 这些特性不错但事实上它们也是危险的。 我们需要一个优秀的面向对象设计(比如 SOLID
、Composite Reuse
等)来保护我们避免这些威胁。然而,面向对象的设计是复杂的,所以InversifyJS
应运而生。
InversifyJS
是一个工具,它能帮助 JavaScript
开发者,写出出色的面向对象设计的代码。
三、目标
InversifyJS
有4个主要目标:
允许JavaScript开发人员编写遵循
SOLID
原则的代码。促进并鼓励遵守最佳的面向对象编程(
OOP
)和依赖注入(IoC
)实践。尽可能少的运行时开销。
提供艺术编程体验和生态。
四、安装
可以使用npm
获得最新的版本和类型定义:
$ npm install inversify reflect-metadata --save
注:由于
InversifyJS
通过反射来获取装饰器的相关元数据,所以需要额外安装库reflect-metadata
。
Inversify npm
包已经包含了 InversifyJS
的类型定义。
:警示⚠️:
InversifyJS
需要TypeScript
版本 >= 2.0 还有experimentalDecorators
,emitDecoratorMetadata
,types
andlib
在tsconfig.json
中compilerOptions
的配置如下:
{
"compilerOptions": {
"target": "es5",
"lib": ["es6"],
"types": ["reflect-metadata"],
"module": "commonjs",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
inversifyjs
需要现代JavaScript
引擎,支持以下特性:
- Reflect metadata
- Map
- Promise (Only required if using provider injection)
- Proxy (Only required if using activation handlers)
如果运行环境不支持这些特性,可能需要导入 shim
或 polyfill
.
:警示⚠️:
reflect-metadata polyfill
应该在整个应用中只导入一次, 因为Reflect
对象需要成为一个全局的单例。
五、应用示例
inversifyjs
基本用法和 API应用示例如下:
步骤 1: 声明接口和类型
目标是编写遵循依赖倒置原则的代码。
这意味着我们应该 ”依赖于抽象而不依赖于具体实现“ 。
先声明一些接口(抽象)。
// file interfaces.ts
interface Warrior {
fight(): string;
sneak(): string;
}
interface Weapon {
hit(): string;
}
interface ThrowableWeapon {
throw(): string;
}
Inversifyjs
需要在运行时使用类型标记作为标识符。接下来将使用 Symbol
作为标识符,也可以使用类或字符串。
// file types.ts
const TYPES = {
Warrior: Symbol.for("Warrior"),
Weapon: Symbol.for("Weapon"),
ThrowableWeapon: Symbol.for("ThrowableWeapon")
};
export { TYPES };
警示⚠️: 推荐使用
Symbol
,但InversifyJS
也支持使用类和字符串字面值。
步骤 2: 使用 @injectable 和 @inject 装饰器声明依赖
接下来,声明一些类,实现刚刚声明的接口。需要使用 @injectable
装饰器去注解。
当一个类依赖于某个接口时,需要使用 @inject
装饰器,来定义在运行时可用的接口标识。在这种情况下,将使用 Symbol
, 如 Symbol.for("Weapon")
和 Symbol.for("ThrowableWeapon")
作为运行时的标识。
// file entities.ts
import { injectable, inject } from "inversify";
import "reflect-metadata";
import { Weapon, ThrowableWeapon, Warrior } from "./interfaces"
import { TYPES } from "./types";
@injectable()
class Katana implements Weapon {
public hit() {
return "cut!";
}
}
@injectable()
class Shuriken implements ThrowableWeapon {
public throw() {
return "hit!";
}
}
@injectable()
class Ninja implements Warrior {
private _katana: Weapon;
private _shuriken: ThrowableWeapon;
public constructor(
@inject(TYPES.Weapon) katana: Weapon,
@inject(TYPES.ThrowableWeapon) shuriken: ThrowableWeapon
) {
this._katana = katana;
this._shuriken = shuriken;
}
public fight() { return this._katana.hit(); }
public sneak() { return this._shuriken.throw(); }
}
export { Ninja, Katana, Shuriken };
如果更喜欢使用属性注入而不是构造函数注入,那就可以不用声明类的构造函数了,属性注入方式如下:
@injectable()
class Ninja implements Warrior {
@inject(TYPES.Weapon) private _katana: Weapon;
@inject(TYPES.ThrowableWeapon) private _shuriken: ThrowableWeapon;
public fight() { return this._katana.hit(); }
public sneak() { return this._shuriken.throw(); }
}
步骤 3: 创建和配置容器
推荐在命名为 inversify.config.ts
的文件中创建和配置容器。这是唯一有耦合的地方。 在项目其余部分中的类,不应该包含对其他类的引用。
// file inversify.config.ts
import { Container } from "inversify";
import { TYPES } from "./types";
import { Warrior, Weapon, ThrowableWeapon } from "./interfaces";
import { Ninja, Katana, Shuriken } from "./entities";
const myContainer = new Container();
myContainer.bind<Warrior>(TYPES.Warrior).to(Ninja);
myContainer.bind<Weapon>(TYPES.Weapon).to(Katana);
myContainer.bind<ThrowableWeapon>(TYPES.ThrowableWeapon).to(Shuriken);
export { myContainer };
步骤 4: 解析依赖
可以使用方法 get<T>
从 Container
中获得依赖。记得应该在项目根结构(尽可能靠近应用程序的入口点的位置)去解析依赖,避免服务器定位反模式。
import { myContainer } from "./inversify.config";
import { TYPES } from "./types";
import { Warrior } from "./interfaces";
const ninja = myContainer.get<Warrior>(TYPES.Warrior);
expect(ninja.fight()).eql("cut!"); // true
expect(ninja.sneak()).eql("hit!"); // true
正如我们所看到的,Katana and Shuriken 被成功的解析和注入进 Ninja。
InversifyJS
支持 ES5
和 ES6
,而且可以在没有 TypeScript
环境下使用。
不做知识的搬运工,只做知识的汇聚者!
注:本文节选自https://doc.inversify.cloud/zh_cn/,学习InversifyJS,看此文足矣!!墙裂推荐!!
六、拓展阅读
- 点赞
- 收藏
- 关注作者
评论(0)