npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

magic-di

v1.4.0-alpha.0

Published

Common dependency injector.

Downloads

7

Readme

common-di

基于 Angular DI 的设计自行实现的一个依赖注入的工具,https://angular.io/guide/dependency-injection。

如果你想在项目中使用工厂模式或者依赖反转,那么必不可少的需要一个依赖注入的工具。这个工具将会帮助你很好的帮助你实现依赖反转,而不在关系那些对象实例化的细节。同时,因为对象的实例化在注册器中进行创建,所以单例模式也很容易在这里实现。

Table of Contents

Background

出于对于 DI 的需要,并且调研市场上的 DI 工具,都有一些不太满意的地方,所以自己写了一个工具来实现 Angular 定义的 DI 模式。

  • Inversify.Js 用法太过于死板,显示的 Token 模式,导致要写很多代码,相较于 API 还是 Angular 的 Token 和 Provider 的设计更容易理解。
  • injection-js 从 Angular 抽离出来的 DI 工具,因为具有 Angular 的 Module 设计,一般的 DI 是不需要要的。

Token

查找实例的唯一标记,类型描述如下:

export type Token = string | symbol | Function;

Provider

为 Token 提供实例的定义,一共有四种类型的 Provider 定义:

export type Provider = 
  ClassProvider | 
  TypeProvider | 
  ValueProvider | 
  FactoryProvider;

ClassProvider

定义一个 Token 使用某个特定的构造函数的时候会用到的 Provider。

export interface ClassProvider {
  token: Token;
  useClass: ConstructorOf<any>;
}

在依赖反转之后,构造函数都依赖抽象而不依赖实例的时候会非常有效。比如下面的例子:

interface Driveable {
  drive(): void;
}

@Injectable()
class Student {
  @Autowired('Driveable')
  mBike: Driveable;

  goToSchool() {
    console.log('go to school');
    mBike.drive();
  }
}

学生对象依赖的是一个可驾驶的交通工具,可以在创建对象的时候提供一个自行车,也可以在创建的时候提供一个汽车:

@Injectable()
class Car implements Driveable {
  drive() {
    console.log('by car')
  }
}

injector.addProviders(Student)
injector.addProviders({
  token: 'Driveable',
  useClass: Car,
})

const student = injector.get(Student);
student.goToSchool(); // print 'go to school by car' 

TypeProvider

token 和 useClass 都是一样的 ClassProvider,一般不会直接用到,使用 Autowired 如果是一个 Function,就会转换成这个类型。

ValueProvider

和 ClassProvider 一样作用,但是直接提供一个对象实例的 Provider。

export interface ValueProvider {
  token: Token;
  useValue: any;
}

FactoryProvider

和 ClassProvider 一样作用,但是提供一个函数进行对象实例创建的 Provider。

export interface FactoryProvider {
  token: Token;
  useFactory: () => any;
}

Install

$ npm install common-di --save

Usage

对 Construtor 进行构造注入

@Injectable()
class A {
  constructor() {
    console.log('Create A');
  }
}

@Injectable()
class B {
  constructor(public a: A){}
}

const injector = new Injector();
injector.addProviders(A, B);

const b = injector.get(B); // print 'Create A'
console.log(b.a instanceof A) // print 'true'

使用 Autowired 进行动态注入

@Injectable()
class A {
  constructor() {
    console.log('Create A');
  }
}

@Injectable()
class B {
  @Autowired()
  a: A;
}

const injector = new Injector();
injector.addProviders(A, B);

const b = injector.get(B);
console.log(b.a instanceof A); // print 'Create A'; print 'true'

单例与多例的用法

@Injectable()
class Single {
  constructor() {}
}

@Injectable({ mutiple: true })
class Mutiple {
  constructor() {}
}

const injector = new Injector();
injector.addProviders(Single, Mutiple);

const single1 = injector.get(Single);
const single2 = injector.get(Single);
console.log(single1 === single2); // print 'true'

const mutiple1 = injector.get(Mutiple);
const mutiple2 = injector.get(Mutiple);
console.log(mutiple1 === mutiple2); // print 'false'

类型依赖抽象而不是依赖实现的用法

const LOGGER_TOKEN = Symbol('LOGGER_TOKEN');

interface Logger {
  log(msg: string): void;
}

@Injectable()
class App {
  @Autowired(LOGGER_TOKEN)
  logger: Logger;
}

@Injectable()
class LoggerImpl implements Logger {
  log(msg: string) {
    console.log(msg);
  }
}

const injector = new Injector();
injector.addProviders(App);
injector.addProviders({
  token: LOGGER_TOKEN,
  useClass: LoggerImpl,
});

const app = injector.get(App);
console.log(app.logger instanceof LoggerImpl); // print 'true'

使用抽象函数作为 Token 进行依赖注入

abstract class Logger {
  abstract log(msg: string): void;
}

@Injectable()
class LoggerImpl implements Logger {
  log(msg: string) {
    console.log(msg);
  }
}

@Injectable()
class App {
  @Autowired()
  logger: Logger;
}

const injector = new Injector();
injector.addProviders(App);
injector.addProviders({
  token: Logger,
  useClass: LoggerImpl,
});

const app = injector.get(App);
console.log(app.logger instanceof LoggerImpl); // print 'true'

API

@Injectable

interface InstanceOpts {
  mutiple?: boolean;
}
function Injectable(opts?: InstanceOpts): ClassDecorator;

@Injectable({ mutiple: true })
class A {}

const injector = new Injector([A]);

const a = injector.get(A);
console.log(injector.hasInstance(a)) // print 'false'

所有需要被 Injector 创建的构造函数都应该使用这个装饰器修饰才可以正常使用,否则会报错

  • mutiple: 是否启用多例模式,一旦启用了多例模式之后,Injector 将不会持有实例对象的引用。

@Autowired

function Autowired(token?: Token): PropertyDecorator;

@Injectable()
class A {}

@Injectable()
class B {
  @Autowired()
  a: A;
}

修饰一个属性会被注册器动态创建依赖实例,而这个依赖实例只有在被使用的时候才会被创建出来。比如上面的例子中,只有访问到 b.a 的时候,才会创建 A 的实例。

需要注意的是,因为 Autowired 依赖着 Injector 的实例,所以只有从 Injector 创建出来的对象可以使用这个装饰器

@Inject

function Inject(token: string | symbol): ParameterDecorator;

interface IA {
  log(): void;
}

@Injectable()
class B {
  constructor(@Inject('IA') a: IA) {}
}

在构造函数进行依赖注入的时候,需要特别指定依赖 Token 的时候的装饰器。当一个构造函数依赖某个抽象,并且这个抽象是在构造函数中传递进来的时候,会需要使用这个装饰器。

Injector.get

多态实现:

interface Injector<T extends Token> {
  get(token: ConstructorOf<any>, args?: ConstructorParameters<T>, opts?: InstanceOpts): TokenResult<T>;
  get(token: T, opts?: InstanceOpts): TokenResult<T>;
}

从 Injector 获取一个对象实例的方法,如果传递的是一个构造函数,第二个参数可以传递构造函数 Arguments 数据,此时将会直接将构造函数创建实例返回,并附加依赖注入的功能,此时的构造函数不需要被 Injectable 装饰也能正常创建对象。例如下面这样:

@Injectable()
class A {}

class B {
  @Autowired()
  a: A;
}

const injector = new Injector([A])
const b = injector.get(B, []);
console.log(b.a instanceof A); // print 'true'

Injector.hasInstance

Injector 中是否具备某个对象的单例引用

Example Readmes

更多的用例可以 点击查看.

Related Efforts

  • Angular - Angular 的 DI 工具使用文档
  • injection-js - 把 Agular 的 DI 抽取出来的单独仓库。
  • InversifyJS - 目前社区中比较受欢迎的 DI 库,但是感觉用法比较麻烦。
  • power-di - 支付宝小程序目前使用的 DI 工具。