LinkedIn上的一项投票显示,开发者使用Angular依赖注入主要有两种方式。虽然还有其他同样有效的视角,但这个结果本身就很有意思——尤其是考虑到受访者大多是经验丰富的开发者。

第一种方式是把所有服务都注册为root级别,全局可用。第二种则是在组件或路由级别局部提供服务。从个人经验来说,我更倾向后者,但它目前有个危险缺陷:如果服务未被提供,Angular会在运行时崩溃,而且并非所有缺失提供者的场景都能被测试覆盖。

打开网易新闻 查看精彩图片

一些采用第一种方式的开发者告诉我,他们从未深入研究过局部服务。这并不奇怪。Angular的DI系统一旦涉及局部服务,理解难度陡增。Thomas Laforge的文章《How Angular Dependency Injection works under the hood》帮了大忙,但即便有经验,我仍然会忘记提供服务,或者误以为上游已提供,直到运行时才发现问题。

这不是技术能力问题,而是Angular最大的弱点之一:依赖注入缺乏类型安全。组件依赖的服务是隐式的、未被追踪的。导入组件时,没有任何机制告诉你它依赖哪些服务,以及那些服务又依赖什么。

局部服务的核心优势在于更好的职责分离,与基于特性的架构高度契合。它们跟随组件或路由的生命周期,能实现自动清理。有时你需要服务在单个特性内共享,而非全局共享——这就是特性级局部服务的价值。

我的偏好是把逻辑尽可能局部化:组件内隔离的逻辑用纯函数;可复用但不需要DI的逻辑仍是函数;需要测试或与子组件共享的特性逻辑用组件级服务;路由范围内共享的用路由级服务;最后才是全局服务。

http://dingyue.ws.126.net/2026/0507/28c21f63g00teoa8o00p8d000hm00dcp.gif

这种分层思路清晰,但Angular的类型系统并不保护你。依赖关系在编译期不可见,运行时才能暴露问题。对于追求可靠性的团队,这成了选择全局服务的理由——牺牲架构灵活性换取确定性。

修复方向很明确:让依赖关系在类型层面可追踪。当组件A依赖服务B,而B又依赖C时,这套链条应该在编译期就能被验证,而非等到用户点击按钮时崩溃。类型安全不是锦上添花,是基础设施级别的需求。