上一节我们学习了didi
框架如何使用子加载器加载子模块的一些知识,还有包括初始化一些基本组件,今天我们继续学习相关的知识,还是从一个例子开始。
const { Injector } = require('didi');
const { expect } = require('chai');
const injector = new Injector([
{
__exports__: [ 'foo', 'bar' ],
'foo': [
'factory',
function(bar) {
return {
bar: bar
};
}
],
'bar': [
'factory',
function(internal) {
return {
internal: internal
};
}
],
'internal': [
'factory',
function() {
return {};
}
]
}
]);
const foo = injector.get('foo');
const bar = injector.get('bar');
const childInjector = injector.createChild([], [ 'foo', 'bar' ]);
const fooFromChild = childInjector.get('foo');
const barFromChild = childInjector.get('bar');
expect(fooFromChild).to.not.equal(foo);
expect(barFromChild).to.not.equal(bar);
expect(fooFromChild.bar).to.equal(barFromChild);
console.log('private injector');
输出如下:

从以上例子可以看出,从父加载器获取的组件和子加载器获取的相同的组件是不同的组件,但是它们的各自的加载器中获取的依赖项都是同一个组件。
下面我们再看看Injector
作用域的问题,我们用下面的例子来说明问题:
const { Injector } = require('didi');
const { expect } = require('chai');
function Foo() {}
Foo.$scope = [ 'request' ];
function createBar() {
return {};
}
createBar.$scope = [ 'session' ];
const injector = new Injector([
{
'foo': [ 'type', Foo ],
'bar': [ 'factory', createBar ]
}
]);
const foo = injector.get('foo');
const bar = injector.get('bar');
const sessionInjector = injector.createChild([], [ 'session' ]);
expect(sessionInjector.get('foo')).to.equal(foo);
expect(sessionInjector.get('bar')).to.not.equal(bar);
const requestInjector = injector.createChild([], [ 'request' ]);
expect(requestInjector.get('foo')).to.not.equal(foo);
expect(requestInjector.get('bar')).to.equal(bar);
console.log('injector scope');
大家注意,这里的作用域加载器有点奇怪,首先我们定义了两个组件foo
和bar
,它们各自的作用域分别是request
和session
,可以看出的如果用session加载器来加载的话,那么同一个session加载器下的所有request作用域的组件都是一样(与父模块获取的组件相比)的,而同一个session加载器下的所有session作用域组件是不一样的。
相反的是,如果使用resuest加载器来加载的话,那么request作用域的组件每次都不一样,而session作用域的组件都是一样的。
下面我们看看组件的定义替换,用下面的例子来说明这个功能如何使用:
const { Injector } = require('didi');
const { expect } = require('chai');
class Foo {
constructor(bar1, baz1) {
this.bar = bar1;
this.baz = baz1;
}
}
function createBlub(foo1) {
return foo1;
}
const base = /** @type ModuleDeclaration */ ({
foo: [ 'type', [ 'bar', 'baz', Foo ] ],
blub: [ 'factory', [ 'foo', createBlub ] ],
baz: [ 'value', 'baz-value' ],
abc: [ 'value', 'abc-value' ]
});
const extension = /** @type ModuleDeclaration */ ({
foo: [ 'type', [ 'baz', 'abc', Foo ] ]
});
const injector = new Injector([ base, extension ]);
const expectedFoo = {
bar: 'baz-value',
baz: 'abc-value'
};
expect(injector.get('foo')).to.deep.equal(expectedFoo);
expect(injector.get('blub')).to.deep.equal(expectedFoo);
console.log('replace definition via override module');
输出如下:

从以上的代码我们看出,foo
组件本来是依赖于bar
和baz
这三个组件的,现在我定义了一个扩展模块之后,这个foo
组件依赖于baz
和abc
组件。也就是说,如果父组件的以前有一个实现方法的话,那么我想扩展这个组件,可以通过定义一个扩展模块来修改父组件的实现,从而让后来的版本兼容以前的版本。
从以上还可以看出的是,这种实现方式真正实现了对扩展开放而对具体的实现隔离。