Component vs Directive Selector
選擇器類型概述
Angular 提供了兩種主要的裝飾器來創建可重用的 UI 元素:
@Component
: 完整的 UI 元件,包含模板、樣式和邏輯@Directive
: 只包含行為和樣式修改的指令
1. Component 選擇器模式
// 1. 元素選擇器(最常見)
@Component({
selector: 'app-hero', // 使用: <app-hero></app-hero>
template: `
<div class="hero">
<h2>{{hero.name}}</h2>
</div>
`
})
export class HeroComponent { }
// 2. 屬性選擇器
@Component({
selector: 'button[appButton]', // 使用: <button appButton>Click me</button>
template: `
<ng-content></ng-content> // 允許內容投影
`,
styleUrls: ['./button.component.css']
})
export class ButtonComponent {
@Input() color: string = 'primary';
// 元件邏輯...
}
// 3. 類選擇器(較少使用)
@Component({
selector: '.app-special', // 使用: <div class="app-special"></div>
template: `
<div>Special content</div>
`
})
export class SpecialComponent { }
2. Directive 選擇器模式
// 1. 屬性型指令(Attribute Directive)
@Directive({
selector: '[appHighlight]' // 注意:需要 app 前綴
})
export class HighlightDirective {
@Input() highlightColor: string = 'yellow';
constructor(private el: ElementRef) { }
@HostListener('mouseenter')
onMouseEnter() {
this.highlight(this.highlightColor);
}
private highlight(color: string) {
this.el.nativeElement.style.backgroundColor = color;
}
}
// 2. 結構型指令(Structural Directive)
@Directive({
selector: '[customIf]' // 注意:不需要 app 前綴
})
export class CustomIfDirective {
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) { }
@Input() set customIf(condition: boolean) {
if (condition) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
}
DOM 渲染比較
1. 使用元件選擇器的 DOM 結構
<!-- 使用元素選擇器的元件 -->
<app-hero> <!-- 外層包裝元素 -->
<div class="hero"> <!-- 模板內容 -->
<h2>Windstorm</h2>
</div>
</app-hero>
<!-- 使用屬性選擇器的元件 -->
<button appButton> <!-- 單一層級 -->
Click me
</button>
2. 使用指令的 DOM 結構
<!-- 屬性型指令 -->
<div appHighlight> <!-- 直接修改現有元素 -->
Hover me to highlight
</div>
<!-- 結構型指令 -->
<div *customIf="condition"> <!-- 條件性渲染 -->
This content is conditionally rendered
</div>
使用場景建議
使用 Component 的情況
需要完整的 UI 元件
需要模板和樣式
需要內容投影(ng-content)
有複雜的元件邏輯
需要生命週期鉤子
@Component({
selector: 'button[appButton]',
template: `
<span class="icon" *ngIf="icon">
<i class="fas fa-{{icon}}"></i>
</span>
<ng-content></ng-content>
<span class="spinner" *ngIf="loading"></span>
`,
styleUrls: ['./button.component.scss']
})
export class ButtonComponent implements OnInit {
@Input() icon?: string;
@Input() loading: boolean = false;
@Output() clicked = new EventEmitter<void>();
ngOnInit() {
// 初始化邏輯
}
// 更多元件方法...
}
使用 Directive 的情況
只需要修改元素行為
只需要添加樣式
需要重用簡單的 DOM 操作
不需要模板
@Directive({
selector: '[appTooltip]'
})
export class TooltipDirective {
@Input('appTooltip') tooltipText: string = '';
constructor(private el: ElementRef) { }
@HostListener('mouseenter')
onMouseEnter() {
// 顯示工具提示
}
@HostListener('mouseleave')
onMouseLeave() {
// 隱藏工具提示
}
}
實踐建議
選擇器命名規範:
Component: 使用有意義的前綴(如
app-
)Attribute Directive: 必須使用
app
前綴Structural Directive: 不使用前綴
DOM 結構考量:
使用屬性選擇器可以減少 DOM 層級
考慮性能和調試需求
功能劃分:
複雜的 UI 邏輯使用 Component
簡單的行為修改使用 Directive
Last updated