Translated the InversifyJS English document with jeff-Tian, ღ(´ ᴗ · ‘)
Warehouse address: github.com/NeoYo/inver… Welcome fork or start
InversifyJS is a powerful and lightweight inversion of control container for JavaScript and Node.js applications written in TypeScript.
InversifyJS: github.com/inversify/I…
InversifyJS official website: inversify.io
InversifyJS Chinese document: www.inversify.cn
Introduction to the
InversifyJS is a lightweight (4KB) inversion of control container (IoC) that can be used to write TypeScript and JavaScript applications. It uses class constructors to define and inject its dependencies. The InversifyJS API is user-friendly and encourages the application of OOP and IoC best practices.
Why InversifyJS?
JavaScript now supports object-oriented programming, class-based inheritance. These are nice features but they are actually dangerous. We need a good object-oriented design (SOLID, Composite Reuse, etc.) to protect us from these threats. However, object-oriented design is complex, so we created InversifyJS.
InversifyJS is a tool that helps JavaScript developers write excellent object-oriented design code.
The target
InversifyJS has four main goals:
-
Allows JavaScript developers to write code that follows SOLID principles.
-
Promote and encourage adherence to best object-oriented programming and dependency injection practices.
-
As little runtime overhead as possible.
-
Provide artistic programming experience and ecology.
The evaluation of
Nate Kohari – Author of Ninject
“Nice work! I’ve taken a couple shots at creating DI frameworks for JavaScript and TypeScript, but the lack of RTTI really hinders things.The ES7 metadata gets us part of the way there (as you’ve discovered). Keep up the great work!”
Author of Michel Weststrate – MobX
Dependency injection like InversifyJS works nicely
Companies that use InversifyJS
The installation
You can use NPM to get the latest version and type definitions:
$ npm install inversify reflect-metadata --save
Copy the code
The Inversify NPM package already contains type definitions for InversifyJS
Warning: Important! InversifyJS requires TypeScript versions >= 2.0 as well as experimentalDecorators, emitDecoratorMetadata, The types and lib compilerOptions configuration in tsconfig.json is as follows:
{
"compilerOptions": {
"target": "es5"."lib": ["es6"]."types": ["reflect-metadata"]."module": "commonjs"."moduleResolution": "node"."experimentalDecorators": true."emitDecoratorMetadata": true}}Copy the code
Inversifyjs requires a modern JavaScript engine that supports the following features
- Reflect metadata
- Map
- Promise (Only required if using provider injection)
- Proxy (Only required if using activation handlers)
If your runtime environment does not support these features, you may need to import Shim or Polyfill
: Warning: Reflect-Metadata Polyfill should only be imported once throughout your application because the Reflect object needs to be a global singleton. More details can be found here.
Look at polyfills, the development environment in the Wiki, and learn from basic examples.
Based on some
Let’s take a look at the basic usage and API of InversifyJS:
Step 1: Declare the interface and type
Our goal is to write code that follows the dependency inversion principle.
This means that we should “rely on abstractions rather than concrete implementations”.
Let’s first declare some interfaces (abstractions).
// file interfaces.ts
interface Warrior {
fight(): string;
sneak(): string;
}
interface Weapon {
hit(): string;
}
interface ThrowableWeapon {
throw() :string;
}
Copy the code
Inversifyjs requires type tags as identifiers at run time. Next, you’ll use Symbol as the identifier, or you can use classes or strings.
// file types.ts
const TYPES = {
Warrior: Symbol.for("Warrior"),
Weapon: Symbol.for("Weapon"),
ThrowableWeapon: Symbol.for("ThrowableWeapon")};export { TYPES };
Copy the code
Caution: Symbol is recommended, but InversifyJS also supports using classes and string literals (see the features section for more information).
Step 2: Use@injectable
和 @inject
Decorators declare dependencies
Let’s declare some classes that implement the interface we just declared. They all need to be annotated with the @ Injectable decorator.
When a class depends on an interface, we also need to use the @Inject decorator to define the interface identity available at runtime. In this case, we will use symbols such as symbol.for (“Weapon”) and symbol.for (“ThrowableWeapon”) as the identifiers for the runtime.
// file entities.ts
import { injectable, inject } from "inversify";
import "reflect-metadata";
import { Weapon, ThrowableWeapon, Warrior } from "./interfaces"
import { TYPES } from "./types";
@injectable(a)class Katana implements Weapon {
public hit() {
return "cut!"; }}@injectable(a)class Shuriken implements ThrowableWeapon {
public throw() {
return "hit!"; }}@injectable(a)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 };
Copy the code
If you prefer to use property injection instead of constructor injection, you don’t have to declare a constructor for your class:
@injectable(a)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(); }}Copy the code
Step 3: Create and configure containers
It is recommended to create and configure containers in a file named inversify.config.ts. That’s the only place where there’s coupling. Classes in the rest of your project should not contain references to other classes.
// 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 };
Copy the code
Step 4: Resolve dependencies
You can get the dependency from the Container using the method get
. Remember that you should resolve dependencies in the root structure, as close as possible to the entry point of the application, to avoid server localization antipatterns.
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
Copy the code
As we saw Katana and Shuriken were successfully parsed and injected into Ninja.
InversifyJS supports ES5 and ES6 and can be used without TypeScript. Go to JavaScript examples to learn more
InversifyJS features and APIS
Let’s take a look at the features of InversifyJS!
Please consult the wiki for more details.
- Class as identifier
- Symbol as a Symbol
- Container API
- Declare container module
- Container snapshot
- Control the life cycle of dependencies
- Declare optional dependencies
- Inject constants or dynamic values
- Inject the constructor of the class
- Into the factory
- Automatic factory
- Injection provider (asynchronous factory)
- Activate the handle
- Constructor rear decorator
- The middleware
- Multiple injection
- Label the binding
- Create your own label decorator
- Naming binding
- The default target
- Support for hierarchical dependency injection systems
- Context binding and @targetName
- Properties into
- Circular dependencies
- inheritance
Please consult the wiki for more details.
ecological
In order to provide an artistic development experience, we also strive to:
- Middleware plug-in
- The development tools
- example
Check out the Ecology Wiki page to learn more.
Support
If you have any problems, we are happy to help. You can use the problems page to report problems.
If you would like to share your thoughts with the development team or join us, you can join the discussion forum. You can also check out the wiki to learn more about InversifyJS.
Acknowledgements
Thanks a lot to all the contributors, all the developers out there using InversifyJS and all those that help us to spread the word by sharing content about InversifyJS online. Without your feedback and support this project would not be possible.
License
License under the MIT License (MIT)
Copyright © 2015-2017 Remo H. Jansen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.