Skip to content

Commit df4e31d

Browse files
committed
Merge pull request ecomfe#16 from ecomfe/develop
merge import feature
2 parents 2d12ce1 + bfc911d commit df4e31d

39 files changed

+6191
-1150
lines changed

README.md

Lines changed: 38 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -203,150 +203,61 @@ require(
203203

204204
```
205205

206-
## API
206+
## $ref 操作符
207207

208-
### new IoC(iocConfig)
209-
210-
#### {Object} iocConfig
211-
ioc 配置
212-
213-
##### {Object} config.components
214-
构件批量配置对象, 其中每个key 为构件id,值为构建配置,配置选项见IoC.prototype.addComponent
215-
216-
##### {Function} config.loader
217-
ioc 的模块加载器,默认使用全局的 require,需要符合 AMD 接口规范
218-
219-
### IoC.prototype.addComponent(id, config)
220-
给容器添加一个构件配置
221-
222-
#### {String} id
223-
构件id,不能重复
224-
225-
#### {Object} config
226-
单个构件配置对象
227-
228-
##### {String} config.module
229-
模块加载的路径,将传给 loader,对 AMD loader,不可使用相对路径(使用的是全局 require)
230-
231-
*** 若配置了config.creator 函数,此配置无效。 ***
232-
233-
##### {Function | String} config.creator
234-
构件的构造函数或工厂函数,若未设置为函数,则使用config.module配置的返回值作为creator;
235-
若 creator 为字符串,则使用 config.module 配置的模块返回值的creator属性值作为 creator;
208+
$ref 用来声明当前构件所依赖的构件,IoC容器获取当前构件时,会自动寻找其依赖并创建依赖,最后将其按照指定的注入方式(构造函数/属性/setter)注入。
236209

237210
```javascript
238-
var myComponentConfig = {
239-
// 最终为 new A.method();
240-
module: 'A',
241-
creator: 'method'
242-
};
243-
```
244-
245-
##### {'transient' | 'singleton' | 'static'} config.scope
246-
构件实例的管理方式,默认为 transient:
247-
- 为 transient,每次获取构件实例时,都会调用 creator返回一个新的实例;
248-
- 为 singleton,表示构件为单例,仅在第一次获取实例时调用一次 creator,之后返回同一个实例;
249-
- 为 static,直接返回 creator。
250-
251-
##### {Boolean=false} config.isFactory
252-
标识 creator 是否为工厂函数,若为工厂,则在创建实例时,直接调用,否则用 new 进行调用。
253-
254-
##### {Array} config.args
255-
调用 creator 时传入的参数,完成构造函数依赖注入。
256-
若在单个参数的对象中设置了 $ref 操作符,则表示依赖某个构件,此时将此参数替换为$ref对应的构件实例
257-
258-
```javascript
259-
var myComponentConfig = {
260-
creator: function (name, b){
261-
this.name = name;
262-
this.b = b;
263-
},
264-
// new creator('string', new B())
265-
args: [ 'string', { $ref: 'B' } ]
266-
};
211+
ioc.addComponent('action', {
212+
module: 'Action',
213+
args: [
214+
{ $ref: 'view' },
215+
{ $ref: 'model' },
216+
]
217+
});
267218
```
268219

269-
##### {Object} config.properties
270-
完成构件实例创建后,需要注入的属性,key 为属性名,值为要注入的属性值,若值为对象且有$ref,则该属性值会被替换为$ref对应的构件实例。
271-
272-
$ref依赖创建后,若构件实例有set${Key}方法,则会直接调用该方法,并将依赖作为参数传入,否则直接执行 instance.${key} = {$refInstance}
220+
如上代码:获取 action 时,将会创建 view 和 model 的依赖,并作为参数传递给 action 的构造函数
273221

274-
```javascript
275222

276-
var myComponentConfig = {
277-
creator: function (){
278-
this.setB = function(b) {
279-
this.b = b;
280-
}
281-
},
282-
properties: {
223+
## $import 操作符
283224

284-
// 实例化 a 后,执行 aInstance.name = 'string';
285-
name: 'string',
286-
287-
// 实例化 a 后,执行 aInstance.setB(b)
288-
B: { $ref: 'B' }
289-
}
290-
};
291-
```
292-
293-
##### {Boolean=false} config.auto
294-
设置是否自动注入,若为 true,则 ioc 容器会在创建构件实例后,寻找实例的 setter(set${Name}),
295-
之后会调用该 setter,将对应的 ${name}依赖作为参数传入 setter。
296-
***注意:properties中的配置优先级高于自动注入***
225+
$import 操作符是为了简化配置而诞生的,在实际场景中经常遇到需要重用一个模块,但仅仅是参数不同,0.1版本时需要重新定义一个构件配置,
226+
使用$import操作符则可以重用现有的构件配置,同时覆盖需要的配置项,IoC 会为$import创建一个匿名的构件配置。
297227

298228
```javascript
299-
var myComponentConfig = {
300-
creator: function () {
301-
this.setB = function(b) {
302-
this.b = b;
303-
}
304-
this.setC = function(c) {
305-
this.c = c;
306-
}
307-
},
308-
// 会自动查找 setter的依赖,这里会查询到 setB 和 setC对应的依赖为'b','c',
309-
// 由于 c 在 propeties 中已经配置,优先级 properties 更高,因此这里不会去实例化构件 c,仅会实例化构件 b,并调用setB(b);
310-
auto: true,
311-
properties: {
312-
c: 'c' // 优先级高于自动注入,因此最终aInstance.c为'c'
313-
}
314-
229+
var components = {
230+
requestStrategy: {
231+
module: 'common/RequestStrategy'
232+
},
233+
appData: {
234+
// ....
235+
properties: {
236+
requestStrategy: {
237+
$import: 'requestStrategy',
238+
args: ['app', 'app']
239+
}
240+
}
241+
},
242+
creativeData: {
243+
// ....
244+
properties: {
245+
requestStrategy: {
246+
$import: 'requestStrategy',
247+
args: ['creative', 'creative']
248+
}
249+
}
250+
}
315251
};
316-
```
317-
318-
### IoC.prototype.addComponent(configs)
319-
给容器批量添加构件配置
320252

321-
#### {Object} configs
322-
键为构件id,值为构件配置
253+
var ioc = IoC({ components: components });
323254

324-
### IoC.prototype.getComponent(ids, cb)
325-
获取构件实例
326-
327-
#### {String | Array} ids
328-
需要获取的构件 id,可为数组或字符串;
329-
为字符串时,获取id 对应的构件,为数组时,批量获取数组中每个字符串 id 对应的构件
330-
331-
#### {Function} cb
332-
构件获取完毕后的回调函数,会将实例按照 id 的传入顺序作为参数依次传递给 cb:
333-
334-
```javascript
335-
ioc.getComponent(['a', 'b'], function(a, b){
336-
337-
});
338255
```
339256

340-
### IoC.prototype.loader(loader)
341-
设置 ioc 实例的模块加载器
342-
343-
### IoC.prototype.dispose
344-
销毁容器,若 scope 为 singleton 的构件有 dispose 方法,ioc会自动调用。
345-
257+
如上代码:appData 与 creativeData 重用了 requestStrategy 的配置,并覆盖了 args 的配置项。
346258

347-
## TODO
348259

349-
- 支持循环依赖的注入
260+
## [API](http://ecomfe.github.io/uioc/doc/IoC.html)
350261

351262

352263

demo/config.js

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,44 @@
33
*/
44
define(
55
{
6-
// 构件名
7-
List: {
8-
// 构件的模块路径
9-
module: 'List',
10-
// 默认为 transient,每次获取构件,都将调用一次 creator
11-
scope: 'transient',
12-
13-
// 将被用来创建实例的函数,falsity的值则调用模块返回的函数作为 creator
14-
// 若传入字符串,则将调用模块对应的方法
15-
// 若传入函数,则该函数作为 creator 创建实例
16-
// creator:
17-
18-
// 传递给构造函数或工厂函数的参数
19-
args: [
20-
{
21-
// 依赖 entityName 构件, 将作为参数注入
22-
$ref: 'entityName'
6+
components: {
7+
// 构件名
8+
List: {
9+
// 构件的模块路径
10+
module: 'List',
11+
// 默认为 transient,每次获取构件,都将调用一次 creator
12+
scope: 'transient',
13+
14+
// 将被用来创建实例的函数,falsity的值则调用模块返回的函数作为 creator
15+
// 若传入字符串,则将调用模块对应的方法
16+
// 若传入函数,则该函数作为 creator 创建实例
17+
// creator:
18+
19+
// 传递给构造函数或工厂函数的参数
20+
args: [
21+
{
22+
// 依赖 entityName 构件, 将作为参数注入
23+
$ref: 'entityName'
24+
}
25+
],
26+
// 属性依赖配置
27+
properties: {
28+
model: { $ref: 'ListModel' },
29+
view: { $ref: 'ListView' }
30+
}
31+
},
32+
ListModel: {
33+
module: 'ListModel'
34+
},
35+
ListView: {
36+
module: 'ListView',
37+
properties: { template: '<li>${name}</li>' }
38+
},
39+
entityName: {
40+
isFactory: true,
41+
creator: function () {
42+
return 'creative';
2343
}
24-
],
25-
// 属性依赖配置
26-
properties: {
27-
model: { $ref: 'ListModel' },
28-
view: { $ref: 'ListView' }
29-
}
30-
},
31-
ListModel: {
32-
module: 'ListModel'
33-
},
34-
ListView: {
35-
module: 'ListView',
36-
properties: { template: '<li>${name}</li>' }
37-
},
38-
entityName: {
39-
isFactory: true,
40-
creator: function () {
41-
return 'creative';
4244
}
4345
}
4446
}

demo/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ define(function (require) {
88
list.enter();
99
});
1010
}
11-
}
11+
};
1212
});

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "uioc",
3-
"version": "0.1.1-alpha.5",
3+
"version": "0.2.0-alpha.1",
44
"description": "an ioc framework",
55
"main": "main.js",
66
"maintainers": {
@@ -15,6 +15,9 @@
1515
"ioc",
1616
"di"
1717
],
18+
"scripts": {
19+
"test": "cd test && karma start --single-run"
20+
},
1821
"author": {
1922
"name": "Exodia",
2023
"email": "d_xinxin@163.com"

src/Container.js

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,35 @@
11
void function (define) {
22
define(
33
function (require) {
4-
var util = require('./util');
4+
var u = require('./util');
55

66
function Container(context) {
77
this.context = context;
88
this.singletons = {};
99
}
1010

11-
Container.prototype.createInstance = function (component) {
11+
Container.prototype.createInstance = function (component, cb) {
1212
if (!component) {
13-
return null;
13+
return cb(null);
1414
}
1515

1616
var id = component.id;
1717
if (component.scope === 'singleton' && this.singletons.hasOwnProperty(id)) {
18-
return this.singletons[id];
18+
return cb(this.singletons[id]);
1919
}
2020

2121
if (component.scope === 'static') {
22-
return component.creator;
22+
return cb(component.creator);
2323
}
2424

25-
var args = createArgs(this, component);
26-
var instance = component.creator.apply(null, args);
27-
if (component.scope === 'singleton') {
28-
this.singletons[id] = instance;
29-
}
30-
return instance;
25+
var me = this;
26+
createArgs(this, component, function (args) {
27+
var instance = component.creator.apply(null, args);
28+
if (component.scope === 'singleton') {
29+
me.singletons[id] = instance;
30+
}
31+
cb(instance);
32+
});
3133
};
3234

3335
Container.prototype.dispose = function () {
@@ -40,17 +42,25 @@ void function (define) {
4042
this.singletons = null;
4143
};
4244

43-
function createArgs(container, component) {
45+
function createArgs(container, component, cb) {
4446
var argConfigs = component.args;
45-
var args = Array(argConfigs.length);
47+
var count = argConfigs.length;
48+
var args = Array(count);
49+
if (!count) {
50+
return cb(args);
51+
}
52+
53+
var done = function (index) {
54+
return function (instance) {
55+
args[index] = instance;
56+
--count === 0 && cb(args);
57+
};
58+
};
59+
4660
for (var i = argConfigs.length - 1; i > -1; --i) {
4761
var arg = argConfigs[i];
48-
args[i] = util.hasReference(arg) ?
49-
container.createInstance(container.context.getComponentConfig(arg.$ref))
50-
: arg;
62+
u.hasReference(arg) ? container.context.getComponent(arg.$ref, done(i)) : done(i)(arg);
5163
}
52-
53-
return args;
5464
}
5565

5666
return Container;

0 commit comments

Comments
 (0)