Certains adorent faire du vĂ©lo et d'autres adorent les rĂ©inventer. Je fais partie de ceux qui rĂ©inventent les vĂ©los pour les rouler. Il y a quelques annĂ©es, j'ai dĂ©jĂ Ă©crit sur Habr Ă propos de mon " vĂ©lo " - un conteneur DI pour JavaScript. La discussion qui a suivi sur les principes de fonctionnement des conteneurs DI et leur diffĂ©rence par rapport au " Service Locator " m'a beaucoup avancĂ© dans la comprĂ©hension du travail de mon propre " vĂ©lo " et a abouti non seulement Ă un certain nombre d' articles sur HabrĂ© ( un , deux , trois , quatre ), mais aussi dans une large mesure l'achĂšvement du " vĂ©lo " lui-mĂȘme.
Sous la coupe - une description du travail du conteneur DI ( @ teqfw / di ) à partir du moment actuel. Limitations : le conteneur est écrit en pur JavaScript (ES2015 +), ne fonctionne qu'avec du code ES2015 +, décoré en modules ES avec l'extension *.mjs
. Avantages : permet de charger et d'utiliser les mĂȘmes modules Ă la fois dans le navigateur et dans les applications nodejs sans transpilation supplĂ©mentaire.
Principes de base des conteneurs DI
Un appel typique Ă un conteneur abstrait d'objets ressemble Ă ceci :
const obj = container.get(id);
La séquence d'actions du conteneur :
Déterminez par id quel type d'objet l'appelant veut obtenir.
Vérifiez la présence de l'objet demandé dans le conteneur, si l'objet est dans le conteneur, renvoyez-le.
Si l'objet doit ĂȘtre créé, utilisez l'ID pour dĂ©terminer oĂč se trouve le fichier contenant le code source de l'objet et tĂ©lĂ©chargez les sources.
( ).
.
.
( ).
.
â 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
Le conteneur @ teqfw / di DI vous permet d'utiliser les deux modules ES eux-mĂȘmes comme dĂ©pendances, ainsi que des Ă©lĂ©ments individuels de l'exportation de modules ES, ainsi que de crĂ©er de nouvelles instances d'objets ou d'utiliser un seul objet pour l'ensemble de l'application. De plus, le mĂȘme code peut ĂȘtre utilisĂ© Ă la fois dans les navigateurs et dans les applications nodejs.
Code type pour un module ES utilisé dans @teqfw/di
:
export default class Mod {
constructor(spec) {
const Clazz = spec['Lib_Dep#'];
const single = spec['Lib_Dep$'];
const inst = spec['Lib_Dep$$'];
// ...
}
}
L'habituel import
peut Ă©galement ĂȘtre utilisĂ© dans le code, mais dans ce cas, le code perd la possibilitĂ© d'ĂȘtre utilisĂ© simultanĂ©ment dans les navigateurs et dans les applications nodejs, car le format d'import du navigateur n'est pas compatible avec le format nodejs.