Angular-SCAM concept
本文开始
在大型的angular Application ,都会使用大量的Module 来将元件需要使用的功能给引用进来,但是,当我们在同一个Module 里面定义多个Component 的时候,就需要从外部引入多个功能进来以让这些Component 都能吃到它们自身需要使用到的功能。
以上这种情况就会导致下面这个问题,假设这个模组有A, B 两个元件,当模组引入A 元件需要使用的功能,但这些功能B 元件都不需要使用,而模组也引入B 元件需要使用的功能,但A 元件都用不到,这样子的情况会导致这个模组要做的事情太杂,且其功能也意义不明。
来举个范例
@NgModule({
providers: [
ConfigService,
PromotionService,
AppService,
],
declarations: [
StringSlicePipe,
CustomerFormatterPipe,
ShoppingCartDealPipe,
CurrencyFormatterPipe,
CustomerCardComponent,
ShoppingCartComponent,
CustomerPortalComponent,
],
})
export class CustomerPortalModule {}假设今天我们除了在CustomerPortalModule 会需要使用到ShoppingCartComponent 以外,在其他的模组中也会需要使用到ShoppingCartComponent 很自然地我们会写出以下程式码
@NgModule({
declarations: [
ShoppingCartComponent,
AnotherPageComponent,
],
})
export class AnotherPageModule {}Ok,加完了,接下来我们先看看ShoppingCartComponent 的建构式里有注入哪些service ,因为也需要引入进这个模组,如此一来ShoppingCartComponent 才能在这个模组正常运作。
@Component({...})
export class ShoppingCartComponent {
...
constructor(private ConfigService) {}
...
}可以看到它有加入ConfigService 这个服务,所以,我们要在AnotherPageModule 引入它
@NgModule({
providers: [
ConfigService // 加入 ConfigService
],
declarations: [
ShoppingCartComponent,
AnotherPageComponent,
],
})
export class AnotherPageModule {}但是,这样还是无法运作!! 因为ConfigService 自己又引入了PromotionService 这个服务,所以,为了ConfigService 我们要再引入PromotionService,所以,再改写一下AnotherPageModule
@NgModule({
providers: [
ConfigService,
PromotionService // 加入 PromotionService
],
declarations: [
ShoppingCartComponent,
AnotherPageComponent,
],
})
export class AnotherPageModule {}接着,我们来看看ShoppingCartComponent 的template,发现它还有用一些pipe 的功能,所以,要再引入这些pipe 到模组中
@NgModule({
providers: [
ConfigService,
PromotionService
],
declarations: [
CurrencyFormatterPipe, // 加入 pipe
CustomerCardComponent, // 加入 pipe
ShoppingCartComponent,
AnotherPageComponent,
],
})
export class AnotherPageModule {}经过以上一连串的引入,有发现了吗?
当我们需要在其他地方使用某个元件的时候,就会要因为该元件有使用和注入的所有功能再次在这个模组再次引入这些功能。
巨大的ShareModule 臭味
而以上这样的问题,也就造就了为什么很多专案里面都会使用ShareModule,在这个Module 里面一次引入所有元件需要使用到的功能,然后,在不同的模组间引入ShareModule 以解决以上的问替。
It kinda works. But 有使用过ShareModule 的方法的人应该都知道,这个ShareModule 最终随着专案扩充,会越长越大~
而这个巨大的ShareModule 就会隐隐地飘出bad code 的臭味(因为还是会落入A 模组引入了shareModule,但是shareModule 里有一半以上的功能A 模组可能都不需要使用)~~
而我们就要透过SCAM 这个概念来解决以上这些问题啰~
什么是SCAM 样板?
它的全名叫做Single Component Angular Module。
这个样板的概念就是,直接让元件客制化属于它自己的Module 。
Angular 的Module 原本的用意,是用来封装该模组中,所包含的元件彼此之间相关的程式码(eg 元件所需的共用方法),但是,我们用ShareModule 的概念把所有不相关的功能通通引入到同一个模组里面,等于直接抛弃原本Angular 模组的精神。
来个简单的范例
@NgModule({
declarations: [
CustomerCardComponent,
CustomerFormatterPipe
],
exports: [CustomerCardComponent]
})
export class CustomerCardComponentModule {}以上这个写法是传统的写法,需要什么就全部引入,因为CustomerCardComponent 需要使用CustomerFormatterPipe 所以,需要引用它。
那现在我们把它们拆开来,
Step 1. 为CustomerFormatterPipe 定义属于它自己的Module
@NgModule({
declarations: [
CustomerFormatterPipe
],
exports: [CustomerFormatterPipe]
})
export class CustomerFormatterPipeModule {}Step 2. 在CustomerCardComponentModule 引入CustomerFormatterPipeModule
接下来,改写一下原本在CustomerCardComponentModule 直接引入CustomerFormatterPipe 的写法
@NgModule({
imports: [
CustomerFormatterPipeModule
],
declarations: [
CustomerCardComponent
],
exports: [CustomerCardComponent]
})
export class CustomerCardComponentModule {}以上就是SCAM 样板的执行方式。
那我们把以上的执行方式,套用到最一开始的ShoppingCartComponent 的范例
Step 1. 为元件定义属于它自己个Module
@NgModule({
// I wont bother with these services, as we really should be making them providedIn: 'root'!
providers: [
ConfigService,
PromotionService,
],
imports: [
CustomerCardComponentModule,
CurrencyFormatterPipeModule,
],
declarations: [
ShoppingCartComponent,
],
export: [ShoppingCartComponent]
})
export class ShoppingCartComponentModule {}Step 2. 引入ShoppingCartComponentModule 模组到CustomerPortalModule 里
@NgModule({
imports: [
ShoppingCartComponentModule // 引入 ShoppingCartComponentModule
],
declarations: [
StringSlicePipe,
CustomerFormatterPipe,
ShoppingCartDealPipe,
// CurrencyFormatterPipe, 可以拿掉
// CustomerCardComponent, 可以拿掉
// ShoppingCartComponent, 可以拿掉
CustomerPortalComponent,
],
exports: [CustomerPortalComponent]
})
export class CustomerPortalModule {}可以看到上面我们透过SCAM 的模板设计方式先建造出属于ShoppingCartComponent 自己的模组后,再直接引入到CustomerPortalModule ,如此一来是不是Module 就可以少写很多额外的程式码,也不用引入东引入西的。
另外,我们在另外一个模组AnotherPageModule 要使用ShoppingCartComponent 也是直接引入ShoppingCartComponentModule 就好,改写如下
@NgModule({
imports: [
ShoppingCartComponentModule // 引入 ShoppingCartComponentModule
],
declarations: [
AnotherPageComponent,
]
})
export class AnotherPageModule {}是不是模组内部干净多了呢~~
PrimeNG UI framework 的SCAM 样板写法
在Angular 专案中,很常用一个叫Primeng 的UI Framework。
有别于这一篇教学文章直接写出一个module.ts 档案,在PrimeNG 里SCAM 样板写法笔者判断应该都是直接将module 定义的内容直接写在各元件的定义档里。
这样一来就不需要用像shareModule 这个一大包的共用Module 了。
来自:https://www.tpisoftware.com/tpu/articleDetails/2894
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!