独立组件时代的新魔鬼:你的「上帝组件」还好吗?#
挖坑现场:在 NgModule 时代,我们有臃肿的「上帝模块」。而在独立组件时代,我们有了新的魔鬼 —— 「上帝组件」 。这是一个独立的「智能组件」,通常是某个页面的入口。它的 imports 数组长达50行,导入了应用中所有能想到的共享组件、指令和管道。它的 .ts 文件里有上千行代码,注入了十几个服务。
尴尬后果:
-
这个组件成了一个微缩版的「巨石」,它与应用的每一个角落都产生了耦合。
-
它几乎无法被复用,也极难进行单元测试。
-
任何一个被它导入的小组件发生破坏性变更,都可能导致这个「上帝组件」崩溃。
解脱之道:坚持「关注点分离原则」。一个组件,应该只做一件事(只有一个关注点),并把它做好。将你的「上帝组件」,毫不留情地拆分成更小、更专注的子组件。让每个组件都成为一个只关注自己职责的「专家」,而不是一个什么都懂一点的「万金油」。
你的libs是「分类书架」,还是「大杂烩垃圾场」?#
挖坑现场:你学习了 Monorepo 的最佳实践,开始使用 libs 文件夹来组织可复用代码,这是一个巨大的进步。但你只是简单地创建了一个 libs/shared,然后把所有组件、服务、工具函数,一股脑地全扔了进去。
尴尬后果:你只是换了一种形式,重新制造了一个旧时代的「大杂烩 SharedModule」。
-
逻辑边界模糊:项目的领域划分不清晰,
shared库最终会变得难以理解和维护。 -
不必要的依赖:
products功能可能只需要shared里的一个PricePipe,但现在它间接依赖了shared里的所有东西。
解脱之道:用「领域驱动」和「类型驱动」的思维,来规划你的 libs! 这才是现代 Angular 模块化的精髓。

一个健康的 libs 结构应该像这样:
libs/
├── products/
│ ├── feature/ # 包含路由、入口组件等「智能」部分
│ ├── ui/ # 可复用的、与产品相关的「木偶」组件 (如 ProductCard)
│ └── data-access/ # 负责获取产品数据的服务和状态管理
├── orders/
│ ├── feature/
│ └── data-access/
└── shared/
├── ui/ # 全局真正可复用的 UI 组件 (如 Button, Avatar)
├── pipes/ # 全局可复用的管道
└── utils/ # 全局可复用的工具函数通过这种方式,你为你的应用建立了清晰的、有层次的「功能地图」和「依赖边界」。
组件间「传参」过度:你是否忘记了依赖注入这位「国之重器」?#
挖坑现场:独立组件的组合是如此简单,以至于你开始滥用它。为了让最顶层的组件,把一个数据传递给第五层的孙子组件,你在中间的每一层组件都定义了 @Input() 和 @Output(),手动地进行「接力传递」。
尴尬后果:
-
极致的耦合:这条链路上的所有组件,都被这个数据「绑架」了。一旦数据的形态需要改变,你需要重构整条链路。
-
代码冗余:中间的组件被迫处理了它们本不关心的逻辑。
解脱之道:永远别忘了依赖注入(DI)这个「国之重器」!
在独立组件时代,DI 的重要性不是降低了,而是提升了。它成为了连接这些「独立王国」的唯一「官方信使」。
如果一个状态需要在多个远距离组件之间共享,正确的做法是:
-
创建一个服务来管理这个状态。
-
让需要这个状态的组件,直接通过
inject()来「召唤」这个服务。
让 DI 成为你的「数据总线」,而不是让你的组件树成为「人肉运输带」。
结语#
模块化是一种思想,是一种架构纪律。在没有了 NgModule 这个「强制」教练之后,我们开发者作为「运动员」,更需要拥有「自我修养」。
在独立组件时代,真正的模块化,是通过有意识的、领域驱动的代码组织,和以依赖注入为核心的彻底解耦来实现的。
独立组件赋予了我们前所未有的自由,但这份自由,也伴随着构建清晰结构的责任。避免落入新的「巨石陷阱」,你的应用才能真正享受到现代化 Angular 带来的轻盈、灵活与强大。
正如老子所言:「合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。」意思是:一人合抱那么粗的大树,也是从细小的萌芽开始长成的;九层高台,也是从堆积泥土开始的;千里的行程,也是从迈开第一步开始的。模块化正是遵循了这一自然法则。将庞大的「巨石应用」分解为一个个清晰定义、职责明确的模块,如同从毫末起步、从累土开始,循序渐进,方能构建出宏伟而稳固的软件大厦。