Skip to content

Commit f987632

Browse files
committed
去掉ContainerModule依赖
1 parent b704939 commit f987632

File tree

3 files changed

+106
-54
lines changed

3 files changed

+106
-54
lines changed

docs/problem.md

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616
```
1717
import getDecorators from "inversify-inject-decorators";
1818
import { Container, injectable, tagged, named } from "inversify";
19-
19+
2020
let container = new Container();
2121
let { lazyInject } = getDecorators(container);
2222
```
23-
从以上代码可以看出,lazyInject不仅仅依赖第三方包,而且还是通过具体的container生成了,这样就会导致lazyInject和具体的container绑定了,这显然是不能接受的,因为我在定义我的服务的时候并不关系最终由哪个contaienr来管理。
23+
24+
从以上代码可以看出,lazyInject 不仅仅依赖第三方包,而且还是通过具体的 container 生成了,这样就会导致 lazyInject 和具体的 container 绑定了,这显然是不能接受的,因为我在定义我的服务的时候并不关系最终由哪个 contaienr 来管理。
2425

2526
## 容器级别的 activation 钩子
2627

@@ -29,3 +30,45 @@ let { lazyInject } = getDecorators(container);
2930
从 2017 年就有的 issue 了,至今还没有解决。
3031

3132
目前也只能通过劫持 bind 函数来实现,但是也不太方便。
33+
34+
## 子容器获取实例时,依赖的实例是默认在子容器还是父容器
35+
36+
[Child-container-resolving problem within hierarchical DI](https://github.com/inversify/InversifyJS/issues/1156)
37+
38+
class Bar 依赖 class Foo
39+
40+
场景 1:
41+
采用自动注入机制,父子容器都没有绑定关系,此时调用 childContainer.get(Bar)
42+
此时 Bar 和 Foo 应该在哪个容易实例化?
43+
44+
场景 2:
45+
采用自动注入机制,父容器绑定了 Bar,此时调用 childContainer.get(Bar)
46+
此时 Bar 和 Foo 应该在哪个容易实例化?
47+
48+
场景 3:
49+
采用自动注入机制,子容器绑定了 Bar,此时调用 childContainer.get(Bar)
50+
此时 Bar 和 Foo 应该在哪个容易实例化?
51+
52+
## 容器获取所有子容器
53+
54+
当前 inversify 没有提供这个方法,所以也不太好实现父组件获取子组件相关的服务实例。
55+
56+
## 不再提供ContainerModule这种方案
57+
58+
```
59+
function bindContainer(container: Container, providers: any) {
60+
if (providers instanceof ContainerModule) {
61+
container.load(providers);
62+
} else if (typeof providers === 'function') {
63+
providers(container);
64+
} else {
65+
for (let i = 0; i < providers.length; i++) {
66+
const s = providers[i];
67+
container.bind(s).toSelf();
68+
}
69+
}
70+
}
71+
```
72+
73+
主要是ContainerModule和直接暴露container从功能上比较重复,没有必要提供重复的功能。
74+
而且还可以减少一个依赖项。

src/index.ts

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import { Container, ContainerModule, interfaces } from 'inversify';
1+
import { Container, interfaces } from 'inversify';
22
import {
33
provide,
44
inject,
55
getCurrentInstance,
66
onUnmounted,
77
reactive,
8-
ref,
98
hasInjectionContext,
109
ComponentInternalInstance,
1110
} from 'vue';
@@ -59,12 +58,10 @@ function getOptions(options?: ContainerOptions) {
5958
return Object.assign({}, DEFAULT_CONTAINER_OPTIONS, options);
6059
}
6160
function makeReactiveObject(_: any, obj: any) {
62-
let res = obj;
63-
if (typeof obj === 'object') {
64-
res = reactive(obj);
65-
} else {
66-
res = ref(obj);
67-
}
61+
// 这里默认obj是一个对象
62+
// 因为当前库只是劫持了to/toSelf方法,所以obj一定是类的实例
63+
// 虽然通过特殊构造函数可以返回非对象的实例,但是这里不考虑这种特殊情况
64+
const res = reactive(obj);
6865
_postReactive(res);
6966
return res;
7067
}
@@ -103,9 +100,7 @@ function reactiveContainer(container: Container) {
103100
return container;
104101
}
105102
function bindContainer(container: Container, providers: any) {
106-
if (providers instanceof ContainerModule) {
107-
container.load(providers);
108-
} else if (typeof providers === 'function') {
103+
if (typeof providers === 'function') {
109104
providers(container);
110105
} else {
111106
for (let i = 0; i < providers.length; i++) {
@@ -161,10 +156,6 @@ export function useRootService<T>(token: interfaces.ServiceIdentifier<T>) {
161156
return getServiceFromContainer(DEFAULT_CONTAINER, token);
162157
}
163158

164-
export function declareProviders(
165-
providers: ContainerModule,
166-
options?: ContainerOptions
167-
): void;
168159
export function declareProviders(
169160
providers: (c: Container) => void,
170161
options?: ContainerOptions
@@ -181,27 +172,25 @@ export function declareProviders(providers: any, options?: ContainerOptions) {
181172
const parent = getContextContainer();
182173
if (parent) {
183174
const instance = getCurrentInstance();
184-
const container = createContainer(parent, { instance, ...options });
175+
let container = createContainer(parent, { instance, ...options });
185176
bindContainer(container, providers);
186177
onUnmounted(() => {
187178
container.unbindAll();
179+
container = null as any;
188180
});
189181
provide(CONTAINER_TOKEN, container);
190182
}
191183
}
192184
}
193-
export function declareRootProviders(providers: ContainerModule): void;
185+
194186
export function declareRootProviders(providers: (c: Container) => void): void;
195187
export function declareRootProviders(
196188
providers: interfaces.Newable<any>[]
197189
): void;
198190
export function declareRootProviders(providers: any) {
199191
bindContainer(DEFAULT_CONTAINER, providers);
200192
}
201-
export function declareAppProviders(
202-
providers: ContainerModule,
203-
options?: ContainerOptions
204-
): void;
193+
205194
export function declareAppProviders(
206195
providers: (c: Container) => void,
207196
options?: ContainerOptions
@@ -219,10 +208,11 @@ export function declareAppProviders(
219208
if (appContainer) {
220209
bindContainer(appContainer, providers);
221210
} else {
222-
const container = createContainer(DEFAULT_CONTAINER, options);
211+
let container = createContainer(DEFAULT_CONTAINER, options);
223212
bindContainer(container, providers);
224213
app.onUnmounted(() => {
225214
container.unbindAll();
215+
container = null as any;
226216
});
227217
app.provide(CONTAINER_TOKEN, container);
228218
}

tests/README.md

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,64 @@
1-
1. createToken创建的token
2-
1. 是否可以在useService自动推导类型
3-
1. 是否可以在inject中自动推导类型
4-
2. CURRENT_COMPONENT注入当前组件实例
5-
3. ContainerOptions不同场景验证
6-
4. 所有待验证场景
1+
2+
1. 所有待验证场景
73
```
8-
1. 当前组件访问当前组件的服务
4+
1. 当前组件访问当前组件的服务 --> test1
95
1. declareProviders + useService
10-
2. 当前组件的服务访问当前组件
11-
1. @inject(CURRENT_COMPONENT)
12-
3. 当前组件的服务访问另一个当前组件的服务
6+
2. 当前组件的服务访问当前组件 --> test2
7+
1. @inject(CURRENT_COMPONENT) 注入当前组件实例
8+
3. 当前组件的服务访问另一个当前组件的服务 --> test3
139
1. @inject(Other_Service)
1410
4. 子组件访问父组件
1511
1. 属于vue原生功能,instance.parent,不推荐使用
16-
5. 子组件访问父组件服务
12+
5. 子组件访问父组件服务 --> test4
1713
1. declareProviders + useService
1814
6. 子组件服务访问父组件
19-
1. 可以实现,但是没有必要,使用场景太少
15+
1. 虽然可以实现,但是没有必要,使用场景太少,所以最终就不实现了
2016
2. 实现原理和@inject(CURRENT_COMPONENT)类似,但是要求每个组件都要把自己注册到容器中,其中key可以通过instance.type获取到当前组件的定义,value就是instance。
21-
7. 子组件服务访问父组件服务
17+
7. 子组件服务访问父组件服务 --> test4
2218
1. @inject(Parent_Service)
23-
8. 父组件访问子组件
24-
1. 属于vue原生功能,通过ref来引用子组件,但是不能引用子组件的子组件
25-
9. 父组件访问子组件服务
19+
8. 父组件访问子组件/孙组件
20+
1. 访问子组件属于vue原生功能,通过ref来引用子组件,但是不能引用子组件的子组件
21+
2. 确定不支持
22+
3. 技术实现起来比较麻烦
23+
9. 父组件访问子组件服务/孙组件服务
2624
1. 未实现
25+
2. 确定不支持,主要是不想要在组件中写过多的业务代码,应该把业务代码都转移到服务中。
26+
3. 技术实现起来比较麻烦
2727
10. 父组件服务访问子组件
2828
1. 未实现
29+
2. 确定不支持
30+
3. 目前只支持获取当前服务所属组件,不支持获取父组件/子组件,因为不期望过多操作组件,而是应该把业务逻辑封装到服务中
2931
11. 父组件服务访问子组件服务
30-
1. 未实现
32+
1. 未实现 - todo
33+
2. 确定要实现,因为父子组件的生命周期不一致,所以不会在父组件的服务中持有子组件服务的实例对象,而是应该实时查询子组件服务
34+
3. 伪代码如下:`this.container.getServiceFromChild(ChildServiceToken)`
35+
3. 伪代码如下:`this.component.getServiceFromChild(ChildServiceToken)`似乎没有必要暴露container对象,直接使用component对象即可
36+
3. 伪代码如下:`getServiceFromChild(this.component, ChildServiceToken)`,以工具方法提供getServiceFromChild
37+
4. 从当前container获取当前组件instance,然后通过instance.subTree来遍历所有子元素
38+
5. 如果子元素是组件,然后通过组件获取是否有对应的container实例
39+
6. 然后通过获取的contaienr实例尝试获取是否有绑定ChildServiceToken,如果有则返回,否则继续查找
40+
7. 应该是container绑定实例,实例也需要绑定container。这样只需要暴露useContainer这个方法就可以了通过container来获取子元素/子服务。
41+
8. 在服务中,则可以通过inject(CURRENT_CONTAINER)获取container实例
42+
9. 问题在与container和实例的相互绑定是否会导致内存泄漏?gc的逻辑是判断对象是否可达,也就是从全局上下文中是否可以访问该对象。
43+
10. 父组件服务持有子组件服务实例对gc不友好,反过来则没有这个问题
44+
11. 不提供useChildService这样的方法,原因同2,10
45+
12. 不提供useContainer和inject(CURRENT_CONTAINER),原因是暂时没有想到暴露container的使用场景
3146
```
32-
5. to方法和toSelf方法验证
33-
6. bindContainer多次调用验证
34-
7. 验证默认容器DEFAULT_CONTAINER
35-
8. 验证useService多次调用结果
36-
9. 验证useService在组件外调用场景
37-
10. 验证useRootService多次调用结果
38-
11. 验证useRootService在组件内/外调用的结果
39-
12. 验证declareProviders多次调用结果
40-
13. 验证declareProviders不同参数的结果
41-
14. 验证declareRootProviders多次调用结果
42-
15. 验证declareRootProviders不同参数的结果
43-
16. 验证declareAppProviders多次调用结果
44-
17. 验证declareAppProviders不同参数的结果
47+
2. createToken创建的token --> todo
48+
1. 是否可以在useService自动推导类型
49+
1. 是否可以在inject中自动推导类型
50+
4. to方法和toSelf方法验证
51+
5. bindContainer多次调用验证
52+
6. 验证默认容器DEFAULT_CONTAINER
53+
7. 验证useService多次调用结果
54+
8. 验证useService在组件外调用场景
55+
9. 验证useRootService多次调用结果
56+
10. 验证useRootService在组件内/外调用的结果
57+
11. 验证declareProviders多次调用结果
58+
12. 验证declareProviders不同参数的结果
59+
13. 验证declareRootProviders多次调用结果
60+
14. 验证declareRootProviders不同参数的结果
61+
15. 验证declareAppProviders多次调用结果
62+
16. 验证declareAppProviders不同参数的结果
4563
17. 全局provide路由变量-route和router
64+
3. ContainerOptions不同场景验证

0 commit comments

Comments
 (0)