Angular新版(v14-v19)發生的那些事

羅慧真 Anita Lo

  • 恆逸教育訓練中心-資深講師
  • 技術分類:程式設計

 

 

Angular 是由 Google 開發和維護的前端框架,專為建構 Single Page Application (SPA) 而設計。它以 TypeScript 為主要語言,並提供強大的工具,使開發者能夠打造動態、模組化且高效能的 Web 應用。
截至本文撰寫時,Angular 已發展至 Angular 19。本篇文章將聚焦於 Angular 14 至 Angular 19 期間最亮眼的更新,包括 Standalone API、@控制流 以及 Signal,並與您一同探討這些新特性的優勢與應用。


Standalone API 的起源

Standalone 元件是在Version 14引入功能預覽版,Version 16進一步強化其功能,最終在Version 17已經正式成為專案的預設模式。

NgModule 的缺點

在了解Standalone API 之前讓我們先來談談為何會有此項發展。早先Angular 採用的是 NgModule,它的缺點是:

  1. 需要額外的樣板代碼
    AppModule.ts
    
    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppComponent } from './app.component';
     
    @NgModule({
      declarations: [AppComponent],
      imports: [BrowserModule],
      bootstrap: [AppComponent]
    })
    export class AppModule {}

    每個模組需要使用 @NgModule 宣告模組的細節。像是使用declarations 指定Module 中所屬的Component、Directive...,使用imports 匯入其他模組,使用providers 設定服務提供者的相關資訊,整體而言寫法相對繁瑣。

  2. 模組宣告限制了靈活的需要

    Angular必須強制開發者必須把所有的元件納入模組。在維護上帶來了一些麻煩,因為必須考慮某一些共享元件是不是需要使用export 以供其它的模組可以使用。

  3. 不利於 Tree Shaking的運作而影響效能

    在編譯的時候相關依賴的模組會完全的被載入,即使那個功能並沒有被使用。 這會影響到效能,在Standalone的模式下這一點被完全的改善,Angular可以更細緻地進行Tree Shaking,減少不必要的程式代碼的載入。


Standalone API的優勢

  1. 省略了 NgModule,直接啟動應用
    • 不再需要 NgModule,直接用 bootstrapApplication() 啟動應用
    • 更直觀、少寫樣板代碼
    • 更適合漸進式增強(Progressive Enhancement)
    import { bootstrapApplication } from '@angular/platform-browser';
    import { AppComponent } from './app.component';
        
    bootstrapApplication(AppComponent);
  2. 更靈活的元件開發
    • Standalone 組件可以獨立存在,不需要 NgModule。
    • 只需在 @Component() 裡使用 standalone: true,即可讓元件直接使用,不需要再手動匯入模組。
    import { Component } from '@angular/core';
    
    @Component({
        selector: 'app-hello',
        standalone: true,
        template: `<h1>Hello Angular</h1>`
    })
    export class HelloComponent {}

    這個元件可以直接在應用中使用,無需 NgModule!

  3. 更靈活的 Lazy Loading

    在 NgModule 架構下, Lazy Loading 通常需要 RouterModule 配合 forChild(),但 Standalone API 讓這個過程變得更直覺:

    import { Route } from '@angular/router';
    import { HomeComponent } from './home.component';
        
    export const routes: Route[] = [
        { path: '', component: HomeComponent },
        { path: 'about', loadComponent: () => import('./about.component').then(m => m.AboutComponent) }
    ];
    
  4. 更好的 Tree Shaking 和效能

    由於 Standalone API 讓元件變得更獨立,Angular 可以更有效地進行 Tree Shaking,只打包真正需要的部分,減少應用程式的大小,提升效能。

下面說明Standalone API的好處以及如何將 ngModule 模式轉換成 Standalone 專案模式

NgModule Standalone API
樣板代碼 需要大量設定 省略 NgModule,簡單直觀
元件管理 需要模組來組織 元件可獨立存在
Lazy Loading 需 RouterModule.forChild() 直接用 loadComponent()
效能 (Tree Shaking) 可能載入不必要的模組 只打包需要的部分
開發體驗 較為繁瑣 更靈活、更直覺
Tree Shaking 是一種程式碼優化技術,用來移除未使用的程式碼(Dead Code Elimination), 減少最終打包的程式碼大小,提升應用效能。它主要應用在 JavaScript 與 TypeScript 的模組系統(ES6 Modules)。

如何將 NgModule 專案轉移成 Standalone 專案?

Angular 團隊建議使用 Standalone API (Angular v19 官網上的建議)若您的專案預備將之轉移成 Standalone 專案模式,那麼可以使用下面這個指令:

ng generate @angular/core:standalone

首先,它會要求您選擇要遷移的類型,有三個選項:

  • Convert all components, directives and pipes to standalone

    轉換專案中所有的 component、directive以及 pipe 檔案為Standalone

  • Remove unnecessary NgModule classes

    刪除 NgModule的類別檔

  • Bootstrap the application using standalone APIs

    使用 Standalone API 啟動應用程式


使用上下按鈕選擇,遷移程序總共要執行三次,按次序選擇項目。


接下來會詢問「Which path in your project should be migrated」,若已經在專案資料夾可以直接按 「Enter」鍵。


執行完成之後,重複執行上一個指令,選第二項,完成之後再重複,選第三項即可完成遷移行動。


下圖是專案檔在完成遷移之後所更新的檔案 app.component.* 、main.ts 已經變更,app.module.ts 被刪除。


這是遷移之後變更的 main.ts 程式碼:

Main.ts
import { importProvidersFrom } from '@angular/core';
import { AppComponent } from './app/app.component';
import { BrowserModule, bootstrapApplication } from '@angular/platform-browser';


bootstrapApplication(AppComponent, {
providers: [importProvidersFrom(BrowserModule)]
})
.catch(err => console.error(err));

這是遷移之後變更的 app.component.ts 程式碼:

app.component.ts
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
standalone: true,
imports: [NgSwitch, NgSwitchDefault, NgSwitchCase]
})
export class AppComponent {
title = 'ngApp';
}

在@Component 中使用 standalone 宣告這個元件使用 standalone 模式,使用 imports 宣告匯入元件所需用的其他元素。本範例是建構在 Version 18 之下,在 Version 19 之中已經可以不需要明確的使用 standalone 宣告,預設就是 standalone 的元件。

Version 19 不需要明確的使用 standalone 宣告,預設就是 standalone 的元件

新的控制流程@for 、 @if...

Angular 指令 (Directives) 是用在 HTML Template 中在執行階段用來解析與渲染內容的指令,包含 *ngFor 、 *ngIf 、 *ngSwitch …, 以下是 *ngFor 的例子:

<div *ngFor="let item of items; let i = index">
{{ i }} - {{ item }}
</div>

以下是 *ngIf 的例子:

<div *ngIf="isLoggedIn; else loginTemplate">
    歡迎回來!
</div>
<ng-template #loginTemplate>
    <div>請登入</div>
</ng-template>

以下是 *ngSwitch 的例子:

<div [ngSwitch]="status">
    <p *ngSwitchCase="'success'">✅ 操作成功!</p>
    <p *ngSwitchCase="'error'">❌ 發生錯誤,請重試。</p>
    <p *ngSwitchCase="'pending'">⏳ 處理中,請稍候...</p>
    <p *ngSwitchDefault>⚠️ 未知狀態。</p>
</div>


為什麼 Angular 需要改為@結構式語法

Angular 17推出了 @for 、@if、@switch … 來取代傳統的 *ngFor 、 *ngIf 、 *ngSwitch (*ngXXX 指令仍可使用,Angular 並未提出何時會淘汰)。

  1. 效能問題:
    • *ngFor 需要動態解析範本 (TemplateRef),在變更檢測(Change Detection)時會重新評估所有的 DOM 結構。
    • *ngIf 會影響渲染的時機,可能造成不必要的變更檢測。

  2. 語法較為冗長:
    • 需要 let 來定義變數,如 let item of items; let i = index。
    • 不能在 ng-container 內直接使用。

  3. 編譯時無法最佳化:
    • *ngFor 和 *ngIf 都是在運行時決定是否渲染,影響效能。
    • @for 和 @if 讓控制流變成編譯時決定,減少運行時開銷。

改成@結構式語法的優勢

舊語法 (*ngFor, *ngIf) 新語法 (@for, @if)
執行時機 運行時(Runtime) 編譯時(Compile-time)
效能 每次變更時需重新解析 更少變更檢測、速度更快
語法簡潔性 需要 *,寫法冗長 更直覺、更易讀
變數作用域 需要 let 來定義 內建 index, first, last 變數

以下是 @for 的例子:

<ul>
    @for (item of items; track item.id) {
        <li>{{ item.id }} - {{ item.name }}</li>
    }
</ul>

以下是 @if 的例子:

@if (isLoggedIn) {
 <div>歡迎回來! </div>
} @else {
 <div>請登入</div>
}

以下是 @switch 的例子:

@switch (status) {
    @case ('success') {
        <p>✅ 操作成功!</p>
    }
    @case ('error') {
        <p>❌ 發生錯誤,請重試。</p>
    }
    @case ('pending') {
        <p>⏳ 處理中,請稍候...</p>
    }
    @default {
        <p>⚠️ 未知狀態。</p>
    }
}

什麼是Signals?

在傳統的 Angular 應用程式中,當資料發生變更時,Angular 會執行變更偵測,以檢查所有繫結的屬性並更新 DOM。這個過程可能會很耗時,特別是當應用程式很大且有很多繫結的時候。

此外,即使只有一小部分資料發生變更,Angular 仍然需要檢查整個元件樹,這可能會導致不必要的效能耗費。

於是Angular 18推出了Signals,目的是為了解決傳統變更偵測所帶來的效能挑戰,它是一個新的反應式技術,能更精確的追蹤資料變更,並僅在必要時更新 DOM。

以下是 Angular Signals 的主要優勢:

  • 更精確的變更偵測:

    Signals 允許追蹤個別屬性的變更,而不是整個元件樹。這意味著只有在實際變更的屬性才會觸發更新,從而減少了不必要的計算。

  • 更高的效能:

    由於 Signals 只更新必要的 DOM 元素,因此可以提高應用程式的效能,特別是在處理大量資料和複雜繫結時。

  • 更簡潔的程式碼:

    Signals 可以讓您的程式碼更簡潔,因為不需要手動管理變更偵測。

  • 更好的除錯體驗:

    Signals 可以讓您更容易追蹤資料變更,從而更容易找到和修復錯誤。


Angular Signals 的核心概念是「信號 (signal)」。信號是一個包含值的物件,您可以讀取和寫入該值。當您寫入一個信號時,它會通知所有訂閱者該值已變更。 在 Angular 中,您可以使用信號來繫結元件的屬性。當信號的值變更時,Angular 會自動更新 DOM。


下面是關於 signal 的一個簡單的範例:

首先,建立一個範例專案,在終端機執行以下命令:

ng new SignalDemo --minimal
  • --minimal,最小化應用程式專案,會生成最少依賴項和配置的應用程式。元件將使用Inline Template、Inline Style 方式建立。

接著打開 app.component.ts ,編輯程式碼如下:

import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  imports: [],
  template: `
    <h1>Welcome to {{title}}!</h1>
    <p>目前時間: {{ currentTime() }}</p>
  `,
  styles: [],
})
export class AppComponent {
  title = 'SignalDemo';
  currentTime = signal(new Date().toLocaleTimeString());

  constructor() {
    setInterval(() => {
        this.currentTime.set(new Date().toLocaleTimeString());
    }, 1000);
  }
}

  • 這個範例是使用 Angular 19 寫的,所以在 @Component 中已經省略了 standalone: true 的宣告了。
  • 使用 signal () 宣告變數並將目前最新的時間放在 signal () 參數之中
    currentTime = signal(new Date().toLocaleTimeString());
  • 使用 set() 改變變數值,當 setInterval() 的每秒鐘到的時候會將最新的時間填入 signal 。
    this.currentTime.set(new Date().toLocaleTimeString());
  • 將signal變數呈現於網頁,注意,有別於一般變數,這裡需要使用 () ,像是呼叫函式一樣以取得其值。
    <p>目前時間: {{ currentTime() }}</p>
  • 使用終端機啟動應用程式:
    ng s -o
  • 執行結果如下:每秒將會跳動最新的時間。

隨著 Standalone API、@控制流 (@if、@for 等) 以及 Signal 的引入,Angular 在效能優化與開發體驗提升上邁出了關鍵一步。

  • Standalone API 簡化了模組管理,使應用架構更靈活、更直覺。
  • @控制流 取代傳統的 *ngIf、*ngFor、*ngSwitch語法更貼近原生 JavaScript,且渲染效能更佳。
  • Signal 帶來更高效的狀態管理方式,避免不必要的變更偵測,提升應用效能與響應性。

這些改進讓 Angular 更符合現代開發趨勢,讓開發者能夠撰寫更精簡、效能更佳的前端應用。如果您還沒開始使用這些新特性,現在正是最佳時機。

值得一提的是,Angular 仍然保持對舊版語法的相容性,這意味著現有專案不會受到影響,開發者可以逐步採用 Standalone API、@控制流 及 Signal,而無需擔心破壞既有程式碼。Angular 官方也持續提供長期支援 (LTS) 與漸進式升級策略,確保開發團隊可以在適合的時機無縫轉換到新架構。

若您對於學習Angular有更多的興趣,歡迎參考恆逸教育訓練中心Angular課程可以了解更多的技巧喔!



您可在下列課程中了解更多技巧喔!