InversifyJS
Thùng chứa inversion of control (IoC) mạnh mẽ và nhẹ cho ứng dụng JavaScript & Node.js được hỗ trợ bởi TypeScript.
Về
InversifyJS là một thùng chứa inversion of control (IoC) nhẹ cho các ứng dụng TypeScript và JavaScript. Một thùng chứa IoC sử dụng một hàm tạo lớp để xác định và tiêm các phụ thuộc của nó. InversifyJS có một API thân thiện và khuyến khích sử dụng các quy tắc OOP và IoC tốt nhất.
Động cơ
JavaScript hiện nay hỗ trợ lập trình hướng đối tượng (OO) với kế thừa dựa trên lớp. Những tính năng này tốt nhưng sự thật là chúng cũng dangerous.
Chúng ta cần một thiết kế OO tốt (SOLID, Composite Reuse, v.v.) để bảo vệ chúng ta khỏi những mối đe dọa này. Vấn đề là thiết kế OO khó khăn và đó cũng là lý do tại sao chúng tôi đã tạo ra InversifyJS.
InversifyJS là một công cụ giúp các nhà phát triển JavaScript viết mã với thiết kế OO tốt.
Triết lý
InversifyJS đã được phát triển với 4 mục tiêu chính:
Cho phép các nhà phát triển JavaScript viết mã tuân theo các nguyên tắc SOLID.
Hỗ trợ và khuyến khích tuân theo các quy tắc OOP và IoC tốt nhất.
Thêm ít khả năng hoạt động thời gian chạy nhất có thể.
Cung cấp một state of the art development experience.
Các lời chứng
Nate Kohari – Tác giả của Ninject
“Làm việc tốt! Tôi đã thử một vài lần tạo các khung DI cho JavaScript và TypeScript, nhưng thiếu RTTI thực sự làm chậm tiến trình. Metadata ES7 đã đưa chúng ta một phần đến đó (như bạn đã khám phá). Hãy tiếp tục công việc tuyệt vời!”
Michel Weststrate – Tác giả của MobX
Dependency injection như InversifyJS hoạt động tốt
📦 Cài đặt
Cảm ơn bạn!
Bạn có thể nhận phiên bản mới nhất và các định nghĩa kiểu sử dụng trình quản lý gói ưa thích của bạn:
> npm install inversify reflect-metadata --save
> yarn add inversify reflect-metadata
> pnpm add inversify reflect-metadata
❕ Gợi ý! Nếu bạn muốn sử dụng phiên bản reflect-metadata an toàn hơn về kiểu dữ liệu, hãy thử @abraham/reflection
Các định nghĩa kiểu InversifyJS đã được bao gồm trong gói npm inversify.
:warning: Quan trọng! InversifyJS yêu cầu TypeScript >= 4.4 và các tùy chọn biên dịch
experimentalDecorators
,emitDecoratorMetadata
,types
, vàlib
trong tệptsconfig.json
của bạn.
{
"compilerOptions": {
"target": "es5",
"lib": ["es6"],
"types": ["reflect-metadata"],
"module": "commonjs",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
InversifyJS yêu cầu một trình chạy JavaScript hiện đại hỗ trợ:
- Reflect metadata
- Map
- Promise (Chỉ yêu cầu nếu sử dụng provider injection)
- Proxy (Chỉ yêu cầu nếu sử dụng activation handlers)
Nếu môi trường của bạn không hỗ trợ một trong những điều này, bạn sẽ cần nhập một shim hoặc polyfill.
:warning: Polyfill
**reflect-metadata**
nên chỉ được nhập một lần duy nhất trong toàn bộ ứng dụng của bạn vì đối tượng Reflect được thiết kế để là một đối tượng singleton toàn cầu. Thêm chi tiết về điều này có thể được tìm thấy tại here.
Hãy xem trang Environment support and polyfills trong wiki và Basic example để biết thêm chi tiết.
Cơ bản
Hãy xem xét việc sử dụng cơ bản và API của InversifyJS với TypeScript:
Bước 1: Khai báo các giao diện và kiểu của bạn
Mục tiêu của chúng tôi là viết mã tuân theo dependency inversion principle. Điều này có nghĩa là chúng ta nên “phụ thuộc vào các trừơng và không phụ thuộc vào các thực thể cụ thể”. Hãy bắt đầu bằng việc khai báo một số giao diện (trừơng).
// file interfaces.ts
export interface Warrior {
fight(): string;
sneak(): string;
}
export interface Weapon {
hit(): string;
}
export interface ThrowableWeapon {
throw(): string;
}
InversifyJS cần sử dụng kiểu như các định danh tại thời gian chạy. Chúng tôi sử dụng các biểu tượng (symbols) như các định danh, nhưng bạn cũng có thể sử dụng lớp và xâu ký tự.
// file types.ts
const TYPES = {
Warrior: Symbol.for("Warrior"),
Weapon: Symbol.for("Weapon"),
ThrowableWeapon: Symbol.for("ThrowableWeapon")
};
export { TYPES };
Cảm ơn bạn!
Ghi chú: Được đề xuất sử dụng các biểu tượng, nhưng InversifyJS cũng hỗ trợ việc sử dụng lớp và chuỗi chữ số (xin vui lòng tham khảo phần tính năng để tìm hiểu thêm).
Bước 2: Khai báo các phụ thuộc bằng cách sử dụng các trang trí @injectable
và @inject
Hãy tiếp tục bằng cách khai báo một số lớp (cụ thể). Các lớp này là các thực hiện của các giao diện mà chúng ta vừa khai báo. Tất cả các lớp phải được đánh dấu bằng trang trí @injectable
.
Khi một lớp có sự phụ thuộc vào một giao diện, chúng ta cũng cần sử dụng trang trí @inject
để xác định một định danh cho giao diện đó sẽ có sẵn tại thời gian chạy. Trong trường hợp này, chúng ta sẽ sử dụng các Biểu tượng Symbol.for("Weapon")
và Symbol.for("ThrowableWeapon")
làm định danh tại thời gian chạy.
// 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 };
Nếu bạn ưa thích, bạn có thể sử dụng phương thức tiêm thuộc tính thay vì tiêm qua hàm tạo để bạn không cần phải khai báo hàm tạo của lớp:
@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(); }
}
Bước 3: Tạo và cấu hình một Container
Chúng tôi đề xuất thực hiện điều này trong một tệp có tên inversify.config.ts
. Đây là nơi duy nhất trong đó có một số kết nối. Trong phần còn lại của ứng dụng của bạn, các lớp của bạn nên không chứa các tham chiếu đến các lớp khác.
// 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 };
Bước 4: Giải quyết các phụ thuộc
Bạn có thể sử dụng phương thức get<T>
từ lớp Container
để giải quyết một phụ thuộc. Hãy nhớ rằng bạn nên làm điều này chỉ trong composition root của bạn để tránh service locator anti-pattern.
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
Như chúng ta có thể thấy, Katana
và Shuriken
đã được giải quyết thành công và được tiêm vào Ninja
.
InversifyJS hỗ trợ ES5 và ES6 và có thể hoạt động mà không cần TypeScript. Hãy truy cập JavaScript example để tìm hiểu thêm!
🚀 Các tính năng và API của InversifyJS
Hãy cùng xem xét các tính năng của InversifyJS!
- Support for classes
- Support for Symbols
- Container API
- Declaring container modules
- Container snapshots
- Controlling the scope of the dependencies
- Declaring optional dependencies
- Injecting a constant or dynamic value
- Injecting a class constructor
- Injecting a Factory
- Auto factory
- Auto named factory
- Injecting a Provider (asynchronous Factory)
- Activation handler
- Deactivation handler
- Post Construct decorator
- Middleware
- Multi-injection
- Tagged bindings
- Create your own tag decorators
- Named bindings
- Default target
- Support for hierarchical DI systems
- Contextual bindings & @targetName
- Property injection
- Circular dependencies
- Inheritance
Vui lòng tham khảo wiki để biết thêm chi tiết.
🧩 Hệ sinh thái
Để cung cấp trải nghiệm phát triển hàng đầu chúng tôi cũng đang làm việc trên:
Vui lòng tham khảo ecosystem wiki page để tìm hiểu thêm.
Hỗ trợ
Nếu bạn gặp bất kỳ vấn đề nào, chúng tôi sẽ rất vui lòng giúp đỡ. Bạn có thể báo cáo sự cố bằng cách sử dụng issues page hoặc chat. Bạn cũng có thể đặt câu hỏi tại Stack overflow bằng cách sử dụng thẻ inversifyjs
.
Nếu bạn muốn chia sẻ ý kiến với đội phát triển hoặc tham gia cùng chúng tôi, bạn có thể làm điều đó bằng cách sử dụng official the mailing list. Bạn có thể kiểm tra wiki để tìm hiểu thêm về bên trong InversifyJS.
Cảm ơn
Cảm ơn rất nhiều tất cả contributors, tất cả các nhà phát triển sử dụng InversifyJS và tất cả những người giúp chúng tôi lan truyền thông điệp bằng cách chia sẻ nội dung về InversifyJS trực tuyến. Mà không có ý kiến phản hồi và sự hỗ trợ của bạn, dự án này sẽ không thể thực hiện được.
Chi tiết tải về:
Tác giả: inversify
Mã nguồn: https://github.com/inversify/InversifyJS
Giấy phép: MIT license