推荐使用:BottleJS - 轻量级依赖注入微容器
BottleJS 是一款强大而小巧的依赖注入容器。它提供了延迟加载、中间件钩子、装饰器以及灵感源自AngularJS模块API和PHP库Pimple的简洁API。如果你喜欢构建组件堆栈而不是全功能框架、喜欢松耦合对象和依赖注入、喜欢直观的API,以及尝试新事物,那么BottleJS将是你不二的选择。
支持环境
BottleJS兼容IE9及以上及所有ECMAScript 5合规浏览器。
安装
BottleJS可用于浏览器或Node.js应用。你可以通过Bower或NPM进行安装:
$ bower install bottlejs
$ npm install bottlejs
此外,它也托管在cdnjs上,可以直接用于你的HTML文件中:
<script src="https://cdnjs.cloudflare.com/ajax/libs/bottlejs/2.0.1/bottle.min.js"></script>
快速入门
让我们从一个简单的例子开始:使用Bottle#service
注册服务。假设我们有一个服务对象构造函数:
var Beer = function() { /* 啤酒服务,好喝! */ };
只需以下几步就可以将其注册到Bottle中:
var bottle = new Bottle();
bottle.service('Beer', Beer);
当你需要这个服务实例时,访问bottle.container.Beer
即可:
bottle.container.Beer;
简单明了,但背后却发生了这些:
- Bottle创建了一个包含工厂函数的提供者。
- 当首次访问
bottle.container.Beer
时,Bottle会查找提供者并执行工厂方法来构建和返回啤酒服务。 - 提供者和工厂被删除,
bottle.container.Beer
属性设置为啤酒服务实例。之后的访问只是一个简单的属性获取。
注入依赖项
如果啤酒服务有依赖怎么办?比如:
var Barley = function() {};
var Hops = function() {};
var Water = function() {};
var Beer = function(barley, hops, water) { /* 啤酒服务,好喝! */ };
你可以这样注册带有依赖的服务:
var bottle = new Bottle();
bottle.service('Barley', Barley);
bottle.service('Hops', Hops);
bottle.service('Water', Water);
bottle.service('Beer', Beer, 'Barley', 'Hops', 'Water');
现在,当你访问bottle.container.Beer
时,Bottle会懒加载所有依赖,并将它们注入到啤酒服务实例后再返回。
使用服务工厂
如果你需要更复杂的逻辑来创建服务,可以使用Bottle#factory
。工厂函数接收容器作为参数,应返回构造好的服务实例:
var bottle = new Bottle();
bottle.service('Barley', Barley);
bottle.service('Hops', Hops);
bottle.service('Water', Water);
bottle.factory('Beer', function(container) {
var barley = container.Barley;
var hops = container.Hops;
var water = container.Water;
barley.halved();
hops.doubled();
water.spring();
return new Beer(barley, hops, water);
});
使用服务提供者
这是Bottle的核心部分。以上提到的Bottle#service
和Bottle#factory
只是提供者的简写形式。通常这两个就足够了,但如有需要对不同环境下的服务进行更精细化控制,可以注册为提供者。通过传递一个构造函数来完成,该构造函数暴露一个$get
方法,$get
方法用作构建服务的工厂。
var bottle = new Bottle();
bottle.service('Barley', Barley);
bottle.service('Hops', Hops);
bottle.service('Water', Water);
bottle.provider('Beer', function() {
// 如果当前环境不支持水,我们需要对其进行填充。
if (waterNotSupported) {
Beer.polyfillWater();
}
// 这是服务工厂。
this.$get = function(container) {
var barley = container.Barley;
var hops = container.Hops;
var water = container.Water;
barley.halved();
hops.doubled();
water.spring();
return new Beer(barley, hops, water);
};
});
装饰器
Bottle支持向提供者管道注入装饰器。装饰器是简单的函数,拦截服务在提供商阶段创建后,但首次被访问之前。该函数应返回服务,或者替换使用的另一个对象。
var bottle = new Bottle();
bottle.service('Beer', Beer);
bottle.service('Wine', Wine);
bottle.decorator(function(service) {
// 此装饰器将应用于啤酒和葡萄酒服务。
service.stayCold();
return service;
});
bottle.decorator('Wine', function(wine) {
// 此装饰器仅影响葡萄酒服务。
wine.unCork();
return wine;
});
中间件
Bottle中间件类似装饰器,但每次从容器访问服务时都会执行。它们接受服务实例和一个next
函数:
var bottle = new Bottle();
bottle.service('Beer', Beer);
bottle.middleware(function(service, next) {
// 每次服务被访问时都会执行此中间件。
console.log('服务被访问了!');
next();
});
bottle.middleware('Beer', function(beer, next) {
// 只影响啤酒服务。
console.log('啤酒?不错。给你的调酒师小费吧...');
next();
});
中间件可以通过向next
函数传递错误对象来抛出异常:
var bottle = new Bottle();
bottle.service('Beer', Beer);
bottle.middleware('Beer', function(beer, next) {
if (beer.hasGoneBad()) {
return next(new Error('啤酒变质了!'));
}
next();
});
// 结果:Uncaught Error: 啤酒变质了!(…)
嵌套瓶子
使用点号表示法,Bottle会为你生成嵌套容器。每个命名部分都将创建一个新的隔离子容器:
var bottle = new Bottle();
var IPA = function() {};
bottle.service('Beer.IPA', IPA);
bottle.container.Beer; // 这是一个新的Bottle.container对象
bottle.container.Beer.IPA; // 服务
bottle.factory('Beer.DoubleIPA', function (container) {
var IPA = container.IPA; // 注意这里的container是指最近的父容器。
})
子容器是独立的
嵌套容器设计为提供不同包之间的隔离。这意味着你不能在一个不同的父容器编写工厂时访问子容器。
var bottle = new Bottle();
var IPA = function() {};
var Wort = function() {};
bottle.service('Ingredients.Wort', Wort);
bottle.factory('Beer.IPA', function(container) {
// container是'Beer',不是根容器,因此:
container.Wort; // undefined
container.Ingredients.Wort; // undefined
});
API
Bottle
pop(name)
根据名称获取Bottle实例。如果不传参,则直接返回当前实例。调用Bottle构造函数作为函数等同于Bottle.pop()
。
参数 | 类型 | 详情 :------------------------|:-----------|:-------- name
(可选) | String | 瓶子的名称。如果提供,Bottle将存储内部实例并返回相同名称的实例。
clear(name)
移除指定名字的Bottle实例,如果存在的话。接下来调用Bottle.pop(name)
将创建新实例。如果不提供名称,将清除所有命名的内部实例。
一般来说,只有在打算重置瓶子实例以添加新的提供者、装饰器等情况时才调用此方法,如测试初始化。
参数 | 类型 | 详情 :----------------------|:-----------|:-------- name
(可选) | String | 瓶子名称。若提供,将删除内部存储的实例,如果之前曾使用Bottle.pop
创建。如果未提供,将删除所有内部存储的实例。
list(container)
prototype.list()
prototype.container.$list()
列出容器上注册的所有常量、值和服务的名称。全局静态版本Bottle.list(bottle.container)
必须传入容器。原型版本使用实例的容器,容器版本则使用自身。
返回一个字符串数组。
参数 | 类型 | 详情 :-----------------------|:-----------|:-------- container
(可选) | Object | 一个bottle.container
。仅当使用全局静态Bottle.list
时必需。原型版本将使用该实例的容器,容器版本将使用自身。
config
全局配置对象。
属性 | 类型 | 默认值 | 详情 :-------------|:-----------|:--------|:-------- strict | Boolean | false
| 开启严格模式。目前仅检查自动注入的依赖是否未定义。
Bottle.prototype
decorators
由瓶子实例注册的装饰器集合。参见decorator(name, func)
下面的描述。
middlewares
由瓶子实例注册的中间件集合。参见middleware(name, func)
下面的描述。
nested
父瓶子实例根据点号表示法注册的嵌套瓶子集合。请参考文档中的“嵌套瓶子”部分。
providerMap
已注册提供者名的集合。Bottle使用此内部结构来确定提供者是否已实例化过。请参阅provider(name, Provider)
下方。
deferred
当前瓶子实例注册的延迟函数集合。参见defer(func)
下方。
constant(name, value)
向容器添加只读值。
参数 | 类型 | 详情 :---------|:----------|:-------- name | String | 值的名称。 value | Any | 需要注册的值。
BottleJS为你提供了构建现代应用程序所需的灵活性和控制力,无论是在浏览器还是服务器端。其强大的API允许你实现复杂的服务管理,同时也保持代码清晰易懂。立即加入数以千计的开发者,享受BottleJS带给你的便捷开发体验吧!