Containor
Simple IoC container for Javascript.
- Supports any programming style.
- Resolve dependecies async.
- Does not make any assumptions on your stack.
- No dependencies!
🎂
Containor weighs just ~2.8kb minified!*
Guide
Installation
npm install containor
Basic usage
import { createContainer } from 'containor'
class Foo {
constructor() {
this.message = 'foo'
}
}
function bar(foo) {
return {
fooMessage: foo.message
}
}
const container = createContainer()
// A dependency can be a function or a class, it will be invoked with 'new' if possible.
container.add('Foo', Foo)
container.add('bar', bar, ['Foo'])
const barInstance = container.get('bar') // An instance of bar with an instance of Foo as an argument
Singletons
Some dependencies you want to use the same instance of everywhere (for instance one that stores state):
function counter() {
let count = 0
return { increment: () => ++count }
}
container.share('counter', counter);
container.get('counter').increment() // returns '1'
container.get('counter').increment() // returns '2' because it's the same instance 👍
Async dependencies
By adding a callback to get
, it waits for the dependency (and all of it's sub-dependencies, this can be nested infinitely deep) to be registered, then runs the callback. If the dependency is already there, it will resolve immediately.
container.get('Foo', foo => {
// Runs as soon as foo is added to the container.
})
container.add('Foo', Foo)
Getting dependencies async is especially handy for client side code, where scripts can load asynchronously.
Raw arguments
Sometimes you just want to pass a raw value to a dependency instead of just other dependencies, you can do this using raw
:
import { raw } from 'containor'
function displayMessage(logger, message) {
logger.log(message)
}
container.add('logger', () => console);
container.add('displayMessage', displayMessage, ['logger', raw('Hello world!')])
container.get('displayMessage') // Logs "Hello world!"
By adding an argument using raw(value)
, it will just pass the value to the dependency instead of trying to resolve it with the container.
Custom constuction
Sometimes you need to pass in other arguments than just instances from the container (like configs or external dependencies). You can pass in a custom function:
container.add('Baz', () => {
const foo = container.get('foo')
return new Baz(foo, config)
})
const baz = container.get('Baz') // Your manually constructed version of Baz 😎
Providers
Providers allow for lazily adding dependecies to the container. They can especially be useful to optimize performance and bundle size.
container.provide(['foo'], () => {
/* ... Load dependencies async somehow */ (foo) => {
container.add('foo', foo)
}
})
container.get('foo', (foo) => {
// Foo will be instantiated async as soon as it is provided
})
When calling container.provide
, you tell the container: "As soon as you need these dependencies, I will add them to the container". This way you save work if you never need the dependecy at all, or later in the application's life cycle.
Note that when calling container.get
without a callback, it will fail if the provider is providing the dependencies async, as the dependencies will not be immediately available.
Including as a script tag
Containor is not hosted on any cdn yet, however if you install containor from npm there is a dist
folder with a containor.js
(for development) and containor.min.js
. When included, containor put on the global window object as Containor
.
const container = Containor.createContainer()
const rawArgument = Containor.raw(123)
API
createContainer(name: String) => container: Container
.add(name: String, constructor: Function, [arguments: Array])
.share(name: String, constructor: Function, [arguments: Array])
.get(name: String, [callback: Function]) => instance: *|void
.provide(names: Array, callback: Function)
* Size measured by bundling with rollup and bublé with uglify. I will be even less when gzipped