Angular 6中TreeShakeable Providers与注入方式对摇树优化的影响
Angular 6+ 可摇树提供者:构造函数注入 vs Injector.get() 的摇树差异
这个问题问到了Angular摇树优化的核心痛点——可摇树提供者的优化逻辑完全依赖AOT编译器的静态代码分析,两种注入方式的结果差异非常明显,咱们拆解来看:
先明确核心逻辑
Angular的@Inject({provideIn: ...})语法之所以能实现摇树,本质是让AOT编译器在编译阶段就能精准识别服务的依赖链路:哪些组件/服务直接依赖了它,有没有被实际使用。如果没有任何静态依赖,摇树工具(比如webpack的Terser)就会把未使用的服务代码从最终打包产物里移除。
1. 构造函数直接注入(消费者1的写法)
这是Angular官方推荐的注入方式,也是最适配摇树优化的写法:
@Component() class MyComponent { constructor(s: MyService) {} }
- AOT编译器能静态识别
MyComponent对MyService的依赖关系——因为依赖直接声明在构造函数参数里,没有任何动态性。 - 编译器会跟踪这个依赖链路:如果
MyComponent被应用实际使用,MyService就会被保留;如果MyComponent没被用到,或者整个MyService没有任何静态依赖,它的代码就会被摇树工具彻底移除。
2. Injector.get() 动态获取(消费者2的写法)
这种动态获取的方式会直接打破摇树优化的基础:
@Component() class MyComponent { constructor(@Inject(Injector) aInjector: Injector) { const s: MyService = aInjector.get(MyService); } }
aInjector.get(MyService)是运行时动态行为,AOT编译器在编译阶段无法静态分析出你到底从Injector中获取了哪些服务(毕竟你完全可以传入一个动态生成的token,编译器根本没法预判)。- 为了避免误删可能用到的代码,编译器会做保守处理:它会认为
MyService可能被用到,不会将其从打包产物中移除——哪怕这个MyComponent从来没被渲染过,或者这个get()调用根本没执行。 - 简单说:用Injector动态获取
provideIn声明的服务,会完全丧失该服务的摇树优化能力,不管有没有实际使用,它都会留在打包文件里。
额外补充
如果确实需要动态获取服务(比如某些条件性依赖场景),可以考虑结合@Optional()或者在懒加载模块中声明局部提供者,但这些方式也没法恢复完整的摇树能力——只要用了动态获取,编译器就没法做精准的静态依赖分析。
内容的提问来源于stack exchange,提问作者Carsten
相关产品推荐
相关产品推荐

