重新認識 Angular - 專案建立與 Component
Angular 20 學習筆記,從專案建立到 Component 基礎
最近開始學習 Angular 20,這是我的學習筆記,會用 React 的概念對照,幫助自己理解。
Angular 20 和我印象中的差異
我對 Angular 的印象還停留在 NgModule、繁瑣的 decorator、Zone.js 的「魔法」,實際接觸 Angular 20 後發現變化非常大:
| 舊 Angular | Angular 20 | 引入版本 |
|---|---|---|
| NgModule 必須 | Standalone Components(預設) | v17 |
*ngIf, *ngFor | @if, @for Control Flow | v17 |
@Input() decorator | input() function | v17.1 |
| RxJS everywhere | Signals 響應式系統 | v16 preview → v20 stable |
| Zone.js 偵測變更 | Zoneless | v18 preview → v20.2 stable |
| Webpack | esbuild + Vite | v17 |
| Karma 測試 | Web Test Runner / Vitest | v20 實驗性 |
這篇用 React 概念對照 Angular 20,很多特性從 v17 就開始引入,v20 才全部穩定
專案建立
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run devAngular 預設 --standalone,不會產生 app.module.ts
專案結構對照
Angular 習慣用資料夾包 component,但 standalone 也可以像 React 一樣單檔案
第一個 Component
// Button.tsx
export function Button({ label }: { label: string }) {
return <button>{label}</button>;
}Angular Component 解析:
| 屬性 | 說明 |
|---|---|
selector | HTML tag 名稱,如 <app-button> |
standalone: true | 不需要 NgModule |
template | 內嵌 HTML template |
input() | 接收父層傳入的資料(類似 props) |
Component 基礎
Template Syntax 對照
| 功能 | React JSX | Angular Template |
|---|---|---|
| 顯示變數 | {count} | {{ count() }} |
| 屬性綁定 | disabled={true} | [disabled]="true" |
| 事件綁定 | onClick={handler} | (click)="handler()" |
| Class 綁定 | className={styles} | [class]="styles" |
| Style 綁定 | style={{ color: 'red' }} | [style.color]="'red'" |
() = 事件輸出、[] = 資料輸入、[()] = 雙向綁定
Control Flow
Angular 17 引入新的 Control Flow 語法,取代 *ngIf / *ngFor
條件渲染
function Profile({ user }) {
return (
<div>
{user ? (
<span>Welcome, {user.name}</span>
) : (
<span>Please login</span>
)}
</div>
);
}列表渲染
function UserList({ users }) {
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}@for 內建 @empty 處理空陣列,track 取代舊的 trackBy function,簡潔很多
Switch 條件
function StatusBadge({ status }) {
switch (status) {
case 'active':
return <span className="green">Active</span>;
case 'pending':
return <span className="yellow">Pending</span>;
default:
return <span className="gray">Unknown</span>;
}
}Component 溝通
Props (Input)
type Props = {
title: string;
count?: number;
};
function Card({ title, count = 0 }: Props) {
return (
<div>
<h2>{title}</h2>
<span>{count}</span>
</div>
);
}
// Usage
<Card title="Hello" count={5} />Events (Output)
type Props = {
onSelect: (id: string) => void;
};
function Item({ onSelect }: Props) {
return (
<button onClick={() => onSelect('123')}>
Select
</button>
);
}
// Usage
<Item onSelect={(id) => console.log(id)} />Two-way Binding (model)
model() 簡化雙向綁定(v17.2 引入)
// toggle.component.ts
@Component({
selector: 'app-toggle',
standalone: true,
template: `
<input
type="checkbox"
[checked]="checked()"
(change)="checked.set($event.target.checked)"
/>
`,
})
export class ToggleComponent {
checked = model<boolean>(false);
}
// Usage: <app-toggle [(checked)]="isEnabled" />model() 讓你用 [()] 語法雙向綁定,不用自己配 input + output
Content Projection
function Card({ children, header }) {
return (
<div className="card">
<div className="card-header">{header}</div>
<div className="card-body">{children}</div>
</div>
);
}
// Usage
<Card header={<h2>Title</h2>}>
<p>Content here</p>
</Card>常見問題
standalone: true 是什麼?還需要 NgModule 嗎?
Angular 17 開始 standalone 是預設值,component 不用宣告在 NgModule 裡,直接 import 就能用。舊專案的 NgModule 還是支援,但新專案建議全部 standalone
為什麼 template 要用 () 和 []?
| 語法 | 意思 | 記法 |
|---|---|---|
[property] | 資料輸入 | 方括號 = 資料「進去」 |
(event) | 事件輸出 | 小括號 = 事件「出來」 |
[()] | 雙向綁定 | 結合 [] 輸入和 () 輸出 |
一眼就能看出資料流向
input() vs @Input() 差在哪?
input() 是新 API(v17.1),回傳 Signal:
- 型別推斷更好
input.required<T>()強制必填- 和 Signals 整合
@Input() 是舊語法,還能用但新專案建議用 input()
系列文章
- 專案建立與 Component ← 目前位置
- Signals 響應式狀態管理
- 路由、資料與表單
- 效能優化與部署