iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
Build on Google AI

2026年,如何利用 Antigravity CLI、Gemini、各項技能及 MCP Server 建構基於 Firebase 的 Angular 應用系列 第 24 篇

Day 24 - 提升 Angular 效能 - 使用 @defer 實現可延遲檢視(Deferrable Views)

  • 分享至 

  • xImage
  •  

當有可用更新時延遲載入 PWA 更新橫幅

PwaUpdateBanner 僅在部署新版本時才會顯示。因此,我們不需要急先載入它;我們可以將它的載入延遲到根元件中的 updateAvailable signal 變為 true 為止。

@defer (when updateAvailable()) {
  <app-pwa-update-banner />
}
@Component({
  selector: 'app-root',
  imports: [PwaUpdateBanner, HeaderComponent, FooterComponent, RouterOutlet],
  templateUrl: './app.html',
  styleUrl: './app.css',
})
export class App {
  readonly #pwaService = inject(PwaUpdateService);
  readonly updateAvailable = this.#pwaService.updateAvailable;
}

我們注入 PwaUpdateService 來監聽 updateAvailable signal。一旦此 signal 變為 true,瀏覽器便會載入 PwaUpdateBanner 元件。

延遲載入 AnalyzerPanelComponent 中的 AltTextPanel

AnalyzerPanelComponent 由用於選取相片的 PhotoPanel,以及在圖片分析完成後顯示的 AltTextPanel 所組成。因此,我們可以將 AltTextPanel 的載入延遲到 analysis signal 包含資料、發生錯誤或載入狀態變為作用中為止。

<div class="panel-grid">
  <app-photo-panel [(analysis)]="analysis" [(error)]="error" [isLoading]="isLoading()" (emitFile)="handleGenerateClick($event)" />

  @defer (when isLoading() || error() || analysis()) {
    <!-- Right Side: Results -->
    <app-alt-text-panel [analysis]="analysis()" [error]="error()" [isLoading]="isLoading()" />
  } @placeholder {
    <div class="panel-container">
      <div class="empty-state">
        <p>Upload an image and click "Generate" to see the results.</p>
      </div>
    </div>
  }
</div>

使用 @defer (when isLoading() || error() || analysis()) 可確保 AltTextPanel 僅在這三個條件中至少滿足一個時才會載入。當運算式評估為 false 時,@placeholder 區塊會顯示靜態訊息:'Upload an image and click "Generate" to see the results.'。因此,RecommendationsDisplayComponent 與其他元件也都會被延遲載入。

別忘了子元件:在 PhotoPanel 中延遲載入子元件

由於 PhotoPanel 本身並未被延遲載入,很容易讓人就此打住,而完全忽略了範本內部等待被延遲載入的巢狀子元件。

PhotoPanel 由 PhotoPickerComponent、TagsDisplayComponent 以及 ObscureFactComponent 組成,但並非所有元件都必須急先載入。

例如,TagsDisplayComponent 只有在實際存在標籤時才需要渲染。在此之前,可以使用輕量級的佔位區塊簡單告知使用者尚未產生任何標籤。

@let tags = parsed?.tags || [];
@let hasTags = tags.length > 0;
@defer (when hasTags) {
    <app-tags-display [tags]="tags" />
} @placeholder {
    <div class="tags-wrapper">
        <h2 class="tags-title">Suggested Tags</h2>
        <p class="no-tags-message">No tags were generated for this image.</p>
    </div>
}

同樣地,ObscureFactComponent 只有在 Firebase AI Logic 回傳冷知識時才應該載入;否則,會以靜態佔位區塊提示使用者上傳圖片以探索冷知識。

@let fact = parsed?.fact;
@defer (when fact) {
    <app-obscure-fact [interestingFact]="fact" />
} @placeholder {
    <div class="obscure-fact-placeholder">
        <h3 class="obscure-fact-title">A surprising or obscure fact about the tags</h3>
        <p class="obscure-fact-empty">Upload an image to discover an obscure fact and generate speech.</p>
    </div>
}

急先載入 DashboardComponent

由於該應用程式沒有獨立的宣傳著陸頁,我們在根路由('/')上急先載入 DashboardComponent。這透過消除了擷取延遲載入 dashboard 分塊所需的額外網路往返時間,大幅改善了 Lighthouse 稽核中的 First Contentful Paint (FCP) 與 Largest Contentful Paint (LCP)。

export const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
  },
  {
    path: '',
    pathMatch: 'full',
    redirectTo: 'dashboard',
  },
  {
    path: '**',
    redirectTo: 'dashboard',
  },
];

遷移前後的打包大小比較

使用 Defer 前

在實作可延遲檢視之前,初始的主要打包檔案大小超過 207 kB。

使用 Defer 後

使用 Defer 後 2

經過效能最佳化後,主要打包檔案降至 191 kB,且各個元件被清楚地拆分為獨立的延遲載入分塊。

因此,在初始頁面載入期間減少了約 50 kB 的傳輸量。這讓應用程式更具高效能。瀏覽器僅在真正需要時才請求元件分塊。此外,當某個元件更新時,只會載入該元件,而不會強迫使用者重新下載整個應用程式的打包檔案。

今天就先到這裡。明天我們將使用 Chrome DevTools MCP 與 Chrome Lighthouse 取得應用程式的效能、SEO、無障礙性以及代理人瀏覽分數。接著,我們將重構程式碼,在桌面版檢視中將分數推向 100 分。

參考資源

使用 @defer 進行延遲載入
Angular 可延遲檢視教學


上一篇
Day 23 - 提升 Angular 效能 - 使用 injectAsync 延遲載入服務
系列文
2026年,如何利用 Antigravity CLI、Gemini、各項技能及 MCP Server 建構基於 Firebase 的 Angular 應用 共 24 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言