哈囉,各位邦友們!
今天的主角是 Angular v17 引入的「可延遲視圖」:@defer
。
它能讓你把「非關鍵、可晚點出現」的區塊推遲載入,改善初始載入與互動速度。
我們會用實戰範例示範「何時載、怎麼載、出錯怎麼辦」。
@defer
與可用的觸發條件。on viewport
與 on interaction
的典型情境。@placeholder
、@loading
、@error
與 prefetch
。@defer
?一句話:把不需要「立刻」出現的畫面延後載入。@defer
區塊會等到特定條件達成時才載入其內容(含依賴的元件/模組),
在此之前可以顯示 @placeholder
骨架、載入中狀態 @loading
,或在錯誤時顯示 @error
區塊。
常見觸發條件:
可搭配 prefetch:
Dashboard
頁面下方元件用 on viewport
延遲在 Dashboard 下方新增一個TopHeroesChart元件,
讓它進入視窗才載入;在此之前顯示骨架或 Loading。
ng g c top-heroes-chart
// src/app/top-heroes-chart/top-heroes-chart.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-top-heroes-chart',
imports: [],
templateUrl: './top-heroes-chart.html',
styleUrl: './top-heroes-chart.scss'
})
export class TopHeroesChart {
}
<!-- src/app/top-heroes-chart/top-heroes-chart.html -->
<section class="chart">
<h3>Top Heroes</h3>
<!-- 這裡可放實際圖表或列表 -->
<ul>
<li>Hero A</li>
<li>Hero B</li>
<li>Hero C</li>
</ul>
</section>
// src/app/dashboard/dashboard.component.ts
import { Component } from '@angular/core';
import { TopHeroesChart } from '../top-heroes-chart/top-heroes-chart';
import { LoadingSpinner } from '../ui/loading-spinner/loading-spinner';
import { MessageBanner } from '../ui/message-banner/message-banner';
@Component({
selector: 'app-dashboard',
imports: [TopHeroesChart, LoadingSpinner, MessageBanner],
templateUrl: './dashboard.component.html',
styleUrl: './dashboard.component.scss',
})
export class DashboardComponent {}
<!-- src/app/dashboard/dashboard.component.html -->
<section class="dashboard">
<h2>Dashboard</h2>
<p class="muted">以下排行榜為次要內容,進入視窗再載入。</p>
@defer (on viewport; prefetch on idle) {
<app-top-heroes-chart />
} @placeholder (minimum 300ms) {
<div class="skeleton skeleton--chart" aria-hidden="true"></div>
} @loading (after 100ms; minimum 400ms) {
<app-loading-spinner label="載入排行榜..." />
} @error {
<app-message-banner type="error">排行榜載入失敗,稍後再試。</app-message-banner>
}
</section>
/* src/app/dashboard/dashboard.component.scss */
.skeleton--chart {
height: 260px;
border-radius: 12px;
background: linear-gradient(
90deg,
#eef1f7 25%,
#f6f8fc 37%,
#eef1f7 63%
);
background-size: 400% 100%;
animation: shimmer 1.25s ease-in-out infinite;
}
@keyframes shimmer {
0% { background-position: 100% 0; }
100% { background-position: 0 0; }
}
說明:
on viewport
:捲動到可視範圍才載入,避免影響 LCP。prefetch on idle
:瀏覽器空檔先抓檔案,真的進入視窗時就不會卡。@placeholder
與 @loading
可設定延遲與最短顯示時間,避免「閃一下」。@placeholder
、@loading
、@error
、prefetch
心法常用參數(區塊標頭內):
after 100ms
:延後顯示(避免瞬間切換造成閃爍)minimum 400ms
:至少顯示多久(避免一閃而過)(on viewport; prefetch on idle)
on viewport
;互動展開的內容用 on interaction
。prefetch
在「使用者可能要點」的元素上(hover/focus)。@defer
與 hydration 能協作,但不要延遲首屏主要內容。@defer
的收益越大(巨大圖表、第三方套件)。常見錯誤與排查:
when true
或在父層就已載入依賴。minimum
或增加 after
。#ref
與 on interaction(refName)
一致。今日小結:@defer
讓我們能以「使用者情境」決定載入時機,
替初始速度與互動體驗找到平衡。把非關鍵、較重的區塊
移到「看得到/需要時」再載入,往往能帶來明顯的體感提升。
參考資料: