File tree Expand file tree Collapse file tree 5 files changed +138
-20
lines changed Expand file tree Collapse file tree 5 files changed +138
-20
lines changed Original file line number Diff line number Diff line change 1
- ## 测试场景
1
+ ## 测试场景-服务注册缺失与应用层服务的行为
2
2
3
- 卸载组件或者卸载app时,会调用unbindAll方法
3
+ ### 测试目的
4
+
5
+ 验证当服务注册缺失时的错误处理,以及应用层级服务的注册、获取和应用卸载时的行为。
6
+
7
+ ### 测试要点
8
+
9
+ 1 . ** 服务注册缺失时的错误处理**
10
+ - 组件尝试使用未通过 ` declareProviders ` 注册的 ` DemoService ` 时,会抛出错误
11
+ - 错误信息清晰指出问题:"No matching binding found for token: DemoService"
12
+
13
+ 2 . ** 应用层级服务的注册与获取**
14
+ - 使用 ` declareAppProvidersPlugin([DemoService, OtherService]) ` 注册应用层级服务
15
+ - 组件中可以直接通过 ` useService ` 获取这些服务
16
+ - 在外部可以使用 ` useAppService(ServiceClass, app) ` 获取应用层级服务
17
+
18
+ 3 . ** 组件级与应用级服务的实例关系**
19
+ - ` DemoService ` 仅在应用层注册,组件中获取到的实例与 ` useAppService ` 获取的相同
20
+ - ` OtherService ` 同时在组件层和应用层注册,组件中优先使用组件层的独立实例
21
+ - 这体现了服务查找的优先级机制
22
+
23
+ 4 . ** 应用卸载与服务清理**
24
+ - 测试中最终调用 ` rootApp.unmount() ` 模拟应用卸载
25
+ - 应用卸载时框架会自动调用内部的 ` unbindAll ` 方法清理所有注册的服务
26
+ - 这确保了应用生命周期结束时资源被正确释放
27
+
28
+ 这个测试用例展示了框架如何处理服务注册缺失、不同层级服务的使用优先级以及应用卸载时的资源清理机制。这有利于防止内存泄漏并确保应用的正确生命周期管理。
Original file line number Diff line number Diff line change 1
- ## 测试场景
1
+ ## 测试场景-使用 LazyToken 解决循环依赖
2
2
3
- 验证循环依赖,LazyServiceIdentifier 也不能解决循环依赖的问题
3
+ ### 测试目的
4
4
5
- 这是一个大问题,我以为属性注入器可以避免循环依赖已经是行业共识了,比如Spring中就是这么处理的 。
5
+ 验证在使用 ` LazyToken ` 的情况下,框架能否正确处理服务间的循环依赖问题 。
6
6
7
- 但是inversify居然不是这么处理的,显然是收集完所有属性之后才会放入缓存系统。
7
+ ### 测试要点
8
8
9
- 比较简单的方案就是采用二级缓存,假设现在的缓存cache是最终的缓存结果。
10
- 那么再增加一层tempCache,这个缓存只是临时存储实例化对象,方便被其他对象依赖时可以从缓存中获取该对象。
9
+ 1 . ** 循环依赖的场景**
10
+ - ` DemoService ` 通过 ` @Inject(new LazyToken(() => TYPES.OtherService)) ` 依赖 ` OtherService `
11
+ - ` OtherService ` 通过 ` @Inject(new LazyToken(() => TYPES.DemoService)) ` 依赖 ` DemoService `
12
+ - 这形成了一个循环依赖的关系图
13
+
14
+ 2 . ** LazyToken 的解决方案**
15
+ - 使用 ` LazyToken ` 延迟依赖的解析和加载
16
+ - 相比于直接注入,延迟注入可以避免在实例化时需要立即解析所有依赖
17
+ - 测试表明这种方式可以成功创建存在循环依赖的组件
18
+
19
+ 3 . ** 框架内部实现**
20
+ - 框架使用提前缓存机制来解决循环依赖
21
+ - 在实例化对象后,立即放入缓存系统,此时还没有注入属性依赖
22
+ - 当出现循环依赖时,可以从缓存中获取已初始化但尚未被完全设置的对象
23
+
24
+ 4 . ** 与Inversify的差异**
25
+ - Inversify的LazyServiceIdentifier 不能解决循环依赖的问题
26
+ - 与依赖注入框架Inversify不同,本框架提供了对循环依赖的支持
27
+ - 这一特性使开发者可以更灵活地设计服务间的依赖关系
28
+
29
+ 这个测试用例展示了框架如何处理服务间的循环依赖,这是一个在依赖注入系统中常见但处理困难的问题。通过 ` LazyToken ` 和二级缓存的实现,框架成功地支持了这一复杂场景。
Original file line number Diff line number Diff line change 1
- ## 测试场景
1
+ ## 测试场景-查找子组件中的服务实例
2
2
3
- 验证循环依赖,LazyServiceIdentifier 也不能解决循环依赖的问题
3
+ ### 测试目的
4
4
5
- 这是一个大问题,我以为属性注入器可以避免循环依赖已经是行业共识了,比如Spring中就是这么处理的 。
5
+ 验证框架提供的全局功能,可以在父组件中查找并操作其子组件中注册的服务实例 。
6
6
7
- 但是inversify居然不是这么处理的,显然是收集完所有属性之后才会放入缓存系统。
7
+ ### 测试要点
8
8
9
- 比较简单的方案就是采用二级缓存,假设现在的缓存cache是最终的缓存结果。
10
- 那么再增加一层tempCache,这个缓存只是临时存储实例化对象,方便被其他对象依赖时可以从缓存中获取该对象。
9
+ 1 . ** 全局查找服务的功能注册**
10
+ - 框架通过全局令牌 ` FIND_CHILD_SERVICE ` 和 ` FIND_CHILDREN_SERVICES ` 提供查找功能
11
+ - 这些令牌可以通过 ` getRootService ` 方法获取对应的查找工具函数
12
+
13
+ 2 . ** 查找单个子组件服务**
14
+ - ` findChildService ` 函数可以查找所有子组件中第一个给定类型的服务实例
15
+ - 测试验证该函数可以正确返回子组件中的 ` ChildService ` 实例
16
+
17
+ 3 . ** 查找所有子组件服务**
18
+ - ` findChildrenServices ` 函数返回所有子组件中给定类型的服务实例数组
19
+ - 本例中验证了包含 3 个 ` ChildComp ` 组件时,可以成功返回 3 个 ` ChildService ` 实例
20
+
21
+ 4 . ** 服务实例的一致性验证**
22
+ - 测试确认通过 ` findChildService ` 获取的单个实例
23
+ - 与通过 ` findChildrenServices ` 获取的第一个实例是相同的
24
+
25
+ 5 . ** 复杂组件嵌套结构的支持**
26
+ - 测试用例中包含了多种组件嵌套方式的 ` ChildComp ` :
27
+ * 带有插槽内容的组件
28
+ * 深层嵌套在多个 div 中的组件
29
+ * 嵌套在 Suspense 中的组件
30
+
31
+ 这个测试用例展示了框架如何在组件树上进行服务实例的递归查找和发现,这一特性在开发复杂组件结构时很有用,使父组件可以方便地操作子组件的服务实例,无需通过复杂的组件通信机制或属性传递。
Original file line number Diff line number Diff line change
1
+ ## 测试场景-@Computed 装饰器与计算属性缓存
2
+
3
+ ### 测试目的
4
+
5
+ 验证 ` @Computed ` 装饰器对类的 getter 方法的缓存优化能力和响应式行为,测试其如何提升服务属性计算性能与响应式更新。
6
+
7
+ ### 测试要点
8
+
9
+ 1 . ** 普通 getter 与 @Computed 修饰的对比**
10
+ - 普通 getter 从不缓存计算结果,每次访问都会重新计算
11
+ - 使用 ` @Computed ` 修饰的 getter 具有缓存能力,当依赖项未变化时不会重新计算
12
+ - 如果依赖项发生变化,` @Computed ` 修饰的属性会自动清除缓存并重新计算
13
+
14
+ 2 . ** 与 Vue 的 reactive 结合**
15
+ - 不使用 ` reactive ` 包装时,` @Computed ` 依然可以正常工作
16
+ - 使用 ` reactive ` 包装后,响应式加强且缓存更智能
17
+ - 测试验证 ` @Computed ` 在各种场景下都可以保持缓存效果
18
+
19
+ 3 . ** 多个计算属性的交互**
20
+ - 同一服务中定义多个使用 ` @Computed ` 的计算属性
21
+ - 每个计算属性单独维护其缓存和依赖项
22
+ - 当共同依赖项变化时,所有相关计算属性都会自动更新
23
+
24
+ 4 . ** 多实例性能**
25
+ - 测试多个类实例之间的计算属性缓存是否独立
26
+ - 确认每个实例的缓存机制只影响其自身属性
27
+ - 验证不同实例之间的独立性和无干扰性
28
+
29
+ 5 . ** 带有 setter 的计算属性**
30
+ - 验证 ` @Computed ` 装饰器对包含 setter 的计算属性的支持
31
+ - 测试 setter 是否能正常工作并触发相关响应式更新
32
+ - 确保 setter 改变依赖项后,缓存能够正确失效并重新计算
33
+
34
+ 这个测试用例展示了 ` @Computed ` 装饰器如何优化服务中计算属性的性能,通过智能缓存和依赖跟踪减少不必要的重复计算,同时确保在相关依赖发生变化时属性能正确更新。这与 Vue 的 computed 属性功能类似,但应用在了服务类中。
Original file line number Diff line number Diff line change 1
- ## 测试场景
1
+ ## 测试场景-服务的 EffectScope 生命周期管理
2
2
3
- 验证循环依赖,LazyServiceIdentifier 也不能解决循环依赖的问题
3
+ ### 测试目的
4
4
5
- 这是一个大问题,我以为属性注入器可以避免循环依赖已经是行业共识了,比如Spring中就是这么处理的 。
5
+ 验证框架为服务提供的 EffectScope 生命周期管理机制,以及属性响应式与组件生命周期的集成 。
6
6
7
- 但是inversify居然不是这么处理的,显然是收集完所有属性之后才会放入缓存系统。
7
+ ### 测试要点
8
8
9
- 比较简单的方案就是采用二级缓存,假设现在的缓存cache是最终的缓存结果。
10
- 那么再增加一层tempCache,这个缓存只是临时存储实例化对象,方便被其他对象依赖时可以从缓存中获取该对象。
9
+ 1 . ** 服务实例关联的 EffectScope**
10
+ - 框架为每个服务实例创建并维护一个 Effect 作用域
11
+ - 可以通过 ` getEffectScope(this) ` API 获取服务实例关联的效果作用域
12
+ - 测试验证这个作用域在服务有效期间一直保持活跃状态
13
+
14
+ 2 . ** 自定义效果的注册与清理**
15
+ - 测试在服务的作用域内注册自定义清理回调
16
+ - 使用 ` onScopeDispose ` 注册的回调只有在作用域销毁时才会触发
17
+ - 服务作用域存在期间,清理回调不会被调用
18
+
19
+ 3 . ** @Computed 装饰器与响应式更新**
20
+ - 服务类中 ` @Computed ` 装饰的计算属性在内部使用了 EffectScope
21
+ - 测试验证不同视图中的计算属性根据依赖项变化正确更新
22
+ - 当 ` count ` 值在点击按钮后由 1 变为 2时,` sum ` 属性䳎 101 自动更新为 102
23
+
24
+ 4 . ** 组件卸载与服务清理**
25
+ - 当组件卸载时,框架自动关闭相关联的服务作用域
26
+ - 验证组件卸载后,服务的 ` scope.active ` 状态变为 ` false `
27
+ - 所有通过 ` onScopeDispose ` 注册的回调被正确触发
28
+
29
+ 这个测试用例展示了框架如何集成 Vue 的 EffectScope 机制来管理服务的响应式效果和生命周期。通过这一机制,服务实例可以在组件卸载时正确地清理其内部的响应式效果和计算属性,避免内存泄漏和无用计算的发生。
You can’t perform that action at this time.
0 commit comments