@adrianhelvik/container 中文文档教程
@adrianhelvik/container
具有延迟创建的依赖项的非 hacky 依赖项容器。
API
Container.prototype.provider(name: string, provider: function)
以提供者函数的形式向容器添加新值。 使用容器中的依赖项调用提供程序。
Container.prototype.constant(name: string, value: any)
以常量值的形式向容器添加新值。
Container.prototype.invoke(fn: function)
使用容器中的依赖项调用给定函数。 这个方法创建一个新的容器,这样我们就可以使用 也提供方法。
const container = new Container()
container.provider('message', ({ who }) => 'Hello ' + who)
container.constant('who', 'world')
container.invoke(({ message }) => {
console.log(message) // logs 'Hello world'
})
Injected: invoke
调用函数被注入。 它允许你调用另一个 以容器的依赖项作为第一个函数 范围。 创建一个新的子容器以允许提供 作用域依赖。
Injected: provide
此方法允许您向容器提供依赖项。 它在当前容器上调用 .constant(k, v)
。
container.invoke(({ invoke, provide }) => {
provide('message', 'Hello on the outside')
invoke(({ invoke, provide }) => {
provide('message', 'Hello on the inside')
invoke(({ message }) => {
console.log(message) // Hello on the inside
})
})
invoke(({ message }) => {
console.log(message) // Hello on the outside
})
})
Container.prototype.extend()
创建一个从当前容器扩展的容器。 来自子容器的依赖是首选。 子容器将在 如果找不到匹配的依赖项,则为父容器 在父容器中。
const container = new Container()
container.provider('foo', () => 42)
const childContainer = container.extend()
childContainer.provider('bar', () => 43)
childContainer.invoke(({ foo, bar }) => {
expect(foo).toBe(42)
expect(bar).toBe(43)
})
childContainer.provider('foo', () => 44)
childContainer.invoke(({ foo }) => {
expect(foo).toBe(44)
})
Container.prototype.keys()
返回当前依赖项的名称 容器。 不包括任何父容器的键。
container.provider('foo', () => 42)
const childContainer = container.extend()
childContainer.provider('bar', () => 43)
expect(childContainer.keys()).toEqual(['bar'])
Container.prototype.get(key)
从容器中获取给定的属性。
const container = new Container()
container.provider('foo', () => 42)
expect(container.get('foo')).toBe(42)
Container.prototype.has(key)
检查容器中是否存在属性。 不调用提供者函数并返回 即使容器中的值未定义也是如此。 这也会检查父容器中的值。
const container = new Container()
let called
container.provider('foo', () => {
called = true
return undefined // Being explicit here
})
expect(container.has('foo')).toBe(true)
expect(called).toBe(false)
Container.prototype.hasOwn(key)
检查容器中是否存在属性。 不调用提供者函数并返回 即使容器中的值未定义也是如此。 不检查父容器的密钥。
const container = new Container()
const child = container.extend()
container.constant('foo', 42)
expect(container.hasOwn('foo')).toBe(true)
expect(child.hasOwn('foo')).toBe(false)
Cyclic dependencies
循环依赖可以通过 invoke 函数来解决。 然而,防止循环依赖是一个很好的主意 首先。
这里的一个重要注意事项是提供者功能必须 在调用循环依赖之前返回。 这可以 可以按照下面的示例完成,方法是 容器中的值是承诺,或者通过 值是函数,其中使用了调用 在返回函数的主体中。
但作为一般经验法则:避免循环依赖
Good example
container.provider('foo', async ({ invoke }) => {
await new Promise(resolve => setTimeout(resolve))
const bar = await invoke(({ bar }) => bar)
return { bar }
})
container.provider('bar', async ({ invoke }) => {
await new Promise(resolve => setTimeout(resolve))
const foo = await invoke(({ foo }) => foo)
return { foo }
})
container.invoke(async ({ foo, bar }) => {
foo = await foo
bar = await bar
expect(foo.bar).toBe(bar)
expect(bar.foo).toBe(foo)
})
Bad example
container.provider('foo', ({ bar }) => {
return { bar }
})
container.provider('bar', ({ foo }) => {
return { foo }
})
expect(() => {
container.get('foo')
}).toThrow(/Maximum call stack size exceeded/)
Example
import Container from '@adrianhelvik/container'
const container = new Container()
container.provider('foo', () => {
console.log('foo injected')
return 10
})
container.constant('bar', 'Hello world')
// The dependency 'foo' is now injected into
// the function and the provider for foo is
// called.
container.invoke(({ foo, bar }) => {
assert.equal(foo, 10)
assert.equal(bar, 'Hello world')
})
@adrianhelvik/container
A non-hacky dependency container with lazily created dependencies.
API
Container.prototype.provider(name: string, provider: function)
Adds a new value to the container in the form of a provider function. The provider is called with the dependencies in the container.
Container.prototype.constant(name: string, value: any)
Adds a new value to the container in the form of a constant value.
Container.prototype.invoke(fn: function)
Invoke the given function with the dependencies in the container. This method creates a new container, so that we can use the provide method as well.
const container = new Container()
container.provider('message', ({ who }) => 'Hello ' + who)
container.constant('who', 'world')
container.invoke(({ message }) => {
console.log(message) // logs 'Hello world'
})
Injected: invoke
The invoke function is injected. It allows you to invoke another function with the dependencies of the container as the first parameter. A new child container is created to allow providing scoped dependencies.
Injected: provide
This method lets you provide a dependency into the container. It calls .constant(k, v)
on the current container.
container.invoke(({ invoke, provide }) => {
provide('message', 'Hello on the outside')
invoke(({ invoke, provide }) => {
provide('message', 'Hello on the inside')
invoke(({ message }) => {
console.log(message) // Hello on the inside
})
})
invoke(({ message }) => {
console.log(message) // Hello on the outside
})
})
Container.prototype.extend()
Create a container that extends from the current one. Dependencies from the child container are preferred. The child container will lookup dependencies in the parent container if no matching dependency is found in the parent container.
const container = new Container()
container.provider('foo', () => 42)
const childContainer = container.extend()
childContainer.provider('bar', () => 43)
childContainer.invoke(({ foo, bar }) => {
expect(foo).toBe(42)
expect(bar).toBe(43)
})
childContainer.provider('foo', () => 44)
childContainer.invoke(({ foo }) => {
expect(foo).toBe(44)
})
Container.prototype.keys()
Returns the names of the dependencies in the current container. Does not include the keys of any parent container.
container.provider('foo', () => 42)
const childContainer = container.extend()
childContainer.provider('bar', () => 43)
expect(childContainer.keys()).toEqual(['bar'])
Container.prototype.get(key)
Gets a given property from the container.
const container = new Container()
container.provider('foo', () => 42)
expect(container.get('foo')).toBe(42)
Container.prototype.has(key)
Checks if a property exists in the container. Does not invoke provider functions and returns true even if the value in the container is undefined. This checks for the value in parent containers as well.
const container = new Container()
let called
container.provider('foo', () => {
called = true
return undefined // Being explicit here
})
expect(container.has('foo')).toBe(true)
expect(called).toBe(false)
Container.prototype.hasOwn(key)
Checks if a property exists in the container. Does not invoke provider functions and returns true even if the value in the container is undefined. Does NOT check parent containers for the key.
const container = new Container()
const child = container.extend()
container.constant('foo', 42)
expect(container.hasOwn('foo')).toBe(true)
expect(child.hasOwn('foo')).toBe(false)
Cyclic dependencies
Cyclic dependencies can be resolved with the invoke function. It is however a very good idea to prevent cyclic dependencies in the first place.
An important note here is that the provider function must return before invoking the cyclic dependency. This could be done either as in the example below, by making the values in the container be promises, or by having the values be functions, where invoke is used in the body of the returned function.
But as a general rule of thumb: Avoid cyclic dependencies
Good example
container.provider('foo', async ({ invoke }) => {
await new Promise(resolve => setTimeout(resolve))
const bar = await invoke(({ bar }) => bar)
return { bar }
})
container.provider('bar', async ({ invoke }) => {
await new Promise(resolve => setTimeout(resolve))
const foo = await invoke(({ foo }) => foo)
return { foo }
})
container.invoke(async ({ foo, bar }) => {
foo = await foo
bar = await bar
expect(foo.bar).toBe(bar)
expect(bar.foo).toBe(foo)
})
Bad example
container.provider('foo', ({ bar }) => {
return { bar }
})
container.provider('bar', ({ foo }) => {
return { foo }
})
expect(() => {
container.get('foo')
}).toThrow(/Maximum call stack size exceeded/)
Example
import Container from '@adrianhelvik/container'
const container = new Container()
container.provider('foo', () => {
console.log('foo injected')
return 10
})
container.constant('bar', 'Hello world')
// The dependency 'foo' is now injected into
// the function and the provider for foo is
// called.
container.invoke(({ foo, bar }) => {
assert.equal(foo, 10)
assert.equal(bar, 'Hello world')
})