深入指南
路由

Router 生命週期與事件

Angular Router 提供了一套完整的生命週期勾點與事件,讓你能回應導覽的變化,並在路由過程中執行自訂邏輯。

常見的 Router 事件

Angular Router 會發出導覽事件,你可以訂閱這些事件來追蹤導覽生命週期。這些事件可透過 Router.events 這個 Observable 取得。本節依時間順序,介紹用於導覽與錯誤追蹤的常見路由生命週期事件。

Events 說明
NavigationStart 導覽開始時發生,包含所請求的 URL。
RoutesRecognized 路由判斷出哪個路由與 URL 相符後發生,包含路由狀態資訊。
GuardsCheckStart 路由守衛階段開始。Router 會評估 canActivate、canDeactivate 等路由守衛。
GuardsCheckEnd 表示守衛評估完成,包含評估結果(允許或拒絕)。
ResolveStart 資料解析階段開始,路由解析器與資源開始擷取資料。
ResolveEnd 資料解析完成,所有必要的資料與阻塞式資源都已可供使用。
NavigationEnd 導覽成功完成時的最終事件,Router 會更新 URL。
NavigationSkipped Router 略過導覽時發生(例如導覽至相同的 URL)。

以下是常見的錯誤事件:

Event 說明
NavigationCancel Router 取消導覽時發生,通常是因為守衛回傳了 false。
NavigationError 導覽失敗時發生,可能是因為路由無效、解析器發生錯誤,或阻塞式資源遭到拒絕。

如需所有生命週期事件的清單,請參閱本指南中的完整表格。

如何訂閱 Router 事件

當你想在特定的導覽生命週期事件中執行程式碼時,可以訂閱 router.events,並檢查事件的執行個體類型:

// Example of subscribing to router events
import {Component, inject, signal, effect} from '@angular/core';
import {Event, Router, NavigationStart, NavigationEnd} from '@angular/router';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';

@Component(/* ... */)
export class RouterEvents {
  private readonly router = inject(Router);

  constructor() {
    // Subscribe to router events and react to events
    this.router.events.pipe(takeUntilDestroyed()).subscribe((event: Event) => {
      if (event instanceof NavigationStart) {
        // Navigation starting
        console.log('Navigation starting:', event.url);
      }
      if (event instanceof NavigationEnd) {
        // Navigation completed
        console.log('Navigation completed:', event.url);
      }
    });
  }
}

NOTE: 從 @angular/router 匯入的 Event 型別,與一般的全域 Event 型別同名,但兩者並不相同;它也與 RouterEvent 型別不同。

如何對路由事件進行除錯

在看不到事件順序的情況下,除錯 Router 導覽問題可能會相當困難。Angular 提供內建的除錯功能,能將所有 Router 事件記錄到主控台,幫助你了解導覽流程,並找出問題發生的位置。

當你需要檢查 Router 事件順序時,可以啟用內部導覽事件的記錄功能來進行除錯。你可以傳入設定選項(withDebugTracing())來設定,藉此啟用所有路由事件的詳細主控台記錄。

import {provideRouter, Routes, withDebugTracing} from '@angular/router';
import {bootstrapApplication} from '@angular/platform-browser';

const appRoutes: Routes = [];
bootstrapApplication(App, {
  providers: [provideRouter(appRoutes, withDebugTracing())],
});

如需更多資訊,請參閱 withDebugTracing 的官方文件。

常見使用情境

Router 事件能在真實世界的應用程式中實現許多實用功能。以下列出一些搭配 Router 事件使用的常見模式。

載入指示器

在導覽期間顯示載入指示器:

import {Component, computed, inject} from '@angular/core';
import {Router, RouterOutlet} from '@angular/router';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  template: `
    @if (isNavigating()) {
      <div class="loading-bar">Loading...</div>
    }
    <router-outlet />
  `,
})
export class App {
  private router = inject(Router);
  isNavigating = computed(() => !!this.router.currentNavigation());
}

分析追蹤

追蹤頁面瀏覽以進行分析:

import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {inject, DestroyRef, Service} from '@angular/core';
import {Router, NavigationEnd} from '@angular/router';

@Service()
export class AnalyticsService {
  private router = inject(Router);
  private destroyRef = inject(DestroyRef);

  startTracking() {
    this.router.events.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((event) => {
      // Track page views when URL changes
      if (event instanceof NavigationEnd) {
        // Send page view to analytics
        this.analytics.trackPageView(event.url);
      }
    });
  }

  private analytics = {
    trackPageView: (url: string) => {
      console.log('Page view tracked:', url);
    },
  };
}

錯誤處理

妥善處理導覽錯誤,並為使用者提供回饋:

import {Component, inject, signal} from '@angular/core';
import {
  Router,
  NavigationStart,
  NavigationError,
  NavigationCancel,
  NavigationCancellationCode,
} from '@angular/router';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';

@Component({
  selector: 'app-error-handler',
  template: `
    @if (errorMessage()) {
      <div class="error-banner">
        {{ errorMessage() }}
        <button (click)="dismissError()">Dismiss</button>
      </div>
    }
  `,
})
export class ErrorHandler {
  private router = inject(Router);
  readonly errorMessage = signal('');

  constructor() {
    this.router.events.pipe(takeUntilDestroyed()).subscribe((event) => {
      if (event instanceof NavigationStart) {
        this.errorMessage.set('');
      } else if (event instanceof NavigationError) {
        console.error('Navigation error:', event.error);
        this.errorMessage.set('Failed to load page. Please try again.');
      } else if (event instanceof NavigationCancel) {
        console.warn('Navigation cancelled:', event.reason);
        if (event.code === NavigationCancellationCode.GuardRejected) {
          this.errorMessage.set('Access denied. Please check your permissions.');
        }
      }
    });
  }

  dismissError() {
    this.errorMessage.set('');
  }
}

所有 Router 事件

以下列出 Angular 中所有可用的 Router 事件,供你參考。這些事件依類別組織,並依照它們在導覽期間通常發生的順序排列。

導覽事件

這些事件追蹤核心的導覽流程,從開始、路由辨識、守衛檢查到資料解析,讓你能掌握導覽生命週期的每個階段。

Event 說明
NavigationStart 發生於導覽開始時
RouteConfigLoadStart 發生於延遲載入路由設定之前
RouteConfigLoadEnd 發生於延遲載入的路由設定載入完成之後
RoutesRecognized 發生於 Router 解析 URL 並辨識出路由時
GuardsCheckStart 發生於守衛階段開始時
GuardsCheckEnd 發生於守衛階段結束時
ResolveStart 發生於解析階段開始時
ResolveEnd 發生於解析階段結束時

啟用事件

這些事件發生在啟用階段,此時路由元件正被建立實例並進行初始化。路由樹中的每個路由(包括父路由與子路由)都會觸發啟用事件。

Event 說明
ActivationStart 發生於路由啟用開始時
ChildActivationStart 發生於子路由啟用開始時
ActivationEnd 發生於路由啟用結束時
ChildActivationEnd 發生於子路由啟用結束時

導覽完成事件

這些事件代表一次導覽嘗試的最終結果。每一次導覽都恰好會以其中一個事件結束,表明該次導覽是成功、被取消、失敗,還是被略過。

Event 說明
NavigationEnd 發生於導覽成功結束時
NavigationCancel 發生於 Router 取消導覽時
NavigationError 發生於導覽因未預期的錯誤而失敗時
NavigationSkipped 發生於 Router 略過導覽時(例如導覽至相同的 URL)

其他事件

還有一個額外的事件,發生在主要導覽生命週期之外,但它仍屬於 Router 事件系統的一部分。

Event 說明
Scroll 發生於捲動期間

後續步驟

進一步了解路由守衛、使用資源擷取資料與常見的 Router 任務。