自転車に乗るのが好きな人もいれば、自転車を改造するのが好きな人もいます。私は、自転車に乗るために再発明した人の一人です。数年前、私はすでに、JavaScript の DI コンテナーである、私のこの「バイク」について Habr に書きました。 DIコンテナと「からそれらの差の動作原理のその後の議論サービスロケータは」かなり自分「の作業を理解する上で私を進めた自転車を」となっただけでなく、Habré(記事の数は1、2、3、4 ) だけでなく、かなりの程度で「バイク」自体の完成。
カットの下 -現時点での DI コンテナー ( @ teqfw / di )の作業の説明。制限: コンテナーは純粋な JavaScript (ES2015 +) で記述され、ES2015 + コードでのみ動作し、拡張子*.mjs
. 利点: 追加のトランスパイルなしで、ブラウザーと nodejs アプリケーションの両方で同じモジュールをロードして使用できます。
DI コンテナの基本
オブジェクトの抽象コンテナへの典型的な呼び出しは次のようになります。
const obj = container.get(id);
コンテナの一連のアクション:
ID によって、呼び出し側が取得したいオブジェクトの種類を決定します。
要求されたオブジェクトの存在についてコンテナーを確認し、オブジェクトがコンテナー内にある場合はそれを返します。
オブジェクトを作成する必要がある場合は、ID を使用してオブジェクトのソース コードを含むファイルの場所を特定し、ソースをダウンロードします。
( ).
.
.
( ).
.
— 5 , , .
ES-
, ES- — .., DI- ES-.
const obj = {name: 'Simple Object'};
class Clazz {
constructor(spec) {
this.name = 'instance from class constructor';
}
}
function Factory(spec) {
return {name: 'instance from factory'};
}
export {
obj as ObjTmpl,
Clazz as default,
Factory,
}
, ES- (, ):
import Def from './es6.mjs';
import {Factory} from './es6.mjs';
import {ObjTmpl} from './es6.mjs';
const spec = {}; // empty specification
const instClass = new Def(spec);
const instFact = Factory(spec);
const instTmpl = Object.assign(ObjTmpl, {});
, DI-, ES-, :
;
;
— , , ( ) , DI- :
constructor(spec) {
const dep = spec['depId'];
}
...
await container.get('dep1');
:
import Container from '@teqfw/di';
const container = new Container();
container.set('dep1', {name: 'first'});
container.set('dep2', {name: 'second'});
const obj = await container.get('dep1');
ES-, (, -).
@teqfw/di
:
: (
connection
,conf
,i18n
, …);
: (
EsModuleId
);
container.set(id, obj)
, ( ).
@teqfw/di
ES- , Module
(. “” “Javascript: ”).
, - . @teqfw/di
#
:
EsModuleId
: ;
EsModuleId#ExportName
:ExportName
EsModuleId
;
EsModuleId#default
EsModuleId#
:default
EsModuleId
;
@teqfw/di
:
class Clazz {
constructor(spec) {}
}
function Factory(spec) {}
, (), — ( )? @teqfw/di
$
:
EsModuleId#ExportName
: (, )ExportName
EsModuleId
.
EsModuleId#ExportName$
: , ( ),ExportName
EsModuleId
.
default
- ES- :
EsModuleId#default$
EsModuleId#$
EsModuleId$
Singleton
( ), . "" -$$
:
EsModuleId$
EsModuleId#ExportName$
: ( ) , .
EsModuleId$$
EsModuleId#ExportName$$
: .
, singleton’ — . DI- global-. - , , DI — .
@teqfw/di
:
let id1 = 'named'; // named singleton been added manually
let id2 = 'EsModId'; // ES module
let id3 = 'EsModId#'; // default export of ES module
let id4 = 'EsModId#name'; // named export of ES module
let id5 = 'EsModId$'; // singleton from default export
let id6 = 'EsModId$$'; // new instance from default export
let id7 = 'EsModId#name$'; // singleton from named export
let id8 = 'EsModId#name$$'; // new instance from named export
, , , ( ). @teqfw/di
:
constructor(spec) {
const named = spec['namedSingleton'];
const inst = spec['EsModId#name$$'];
const single = spec['EsModId$'];
}
@teqfw/di
, , , , . , , .
ES-, . @teqfw/di
:
container.addSourceMapping('EsModId', './relative/path');
container.addSourceMapping('EsModId', '/absolute/path', true);
( ) , , — nodejs-. , .
, , , namespace’, _
:
EsModId_PathTo_Mod => /absolute/path/PathTo/Mod.mjs
@ teqfw / di DI コンテナーを使用すると、ES モジュール自体と、ES モジュールのエクスポートからの個々の要素の両方を依存関係として使用したり、オブジェクトの新しいインスタンスを作成したり、アプリケーション全体に単一のオブジェクトを使用したりできます。さらに、同じコードをブラウザーと nodejs アプリケーションの両方で使用できます。
で使用される ES モジュールの典型的なコード@teqfw/di
:
export default class Mod {
constructor(spec) {
const Clazz = spec['Lib_Dep#'];
const single = spec['Lib_Dep$'];
const inst = spec['Lib_Dep$$'];
// ...
}
}
通常のimport
ものもコードで使用できますが、この場合、コードはブラウザーと nodejs アプリケーションで同時に使用できなくなります。ブラウザのインポート形式は、nodejs 形式と互換性がありません。