angular-ssr
Angular v20以降で`@angular/ssr`を使用したサーバーサイドレンダリングとハイドレーションを実装します。SSRのセットアップ、ハイドレーション戦略、静的ページのプリレンダリング、ブラウザ専用APIの取り扱いに活用してください。SSRの設定、ハイドレーションの不整合修正、ルートのプリレンダリング、またはコードをSSR対応にする際にトリガーされます。
description の原文を見る
Implement server-side rendering and hydration in Angular v20+ using @angular/ssr. Use for SSR setup, hydration strategies, prerendering static pages, and handling browser-only APIs. Triggers on SSR configuration, fixing hydration mismatches, prerendering routes, or making code SSR-compatible.
SKILL.md 本文
Angular SSR
Angular v20+ で サーバーサイドレンダリング、ハイドレーション、プリレンダリングを実装します。
セットアップ
既存プロジェクトに SSR を追加
ng add @angular/ssr
以下が追加されます:
@angular/ssrパッケージserver.ts- Express サーバーsrc/main.server.ts- サーバーブートストラップsrc/app/app.config.server.ts- サーバープロバイダーangular.jsonに SSR 設定を追加
プロジェクト構成
src/
├── app/
│ ├── app.config.ts # ブラウザ設定
│ ├── app.config.server.ts # サーバー設定
│ └── app.routes.ts
├── main.ts # ブラウザブートストラップ
├── main.server.ts # サーバーブートストラップ
server.ts # Express サーバー
設定
app.config.server.ts
import { ApplicationConfig, mergeApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { provideServerRoutesConfig } from '@angular/ssr';
import { appConfig } from './app.config';
import { serverRoutes } from './app.routes.server';
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
provideServerRoutesConfig(serverRoutes),
],
};
export const config = mergeApplicationConfig(appConfig, serverConfig);
サーバールート設定
// app.routes.server.ts
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
{
path: '',
renderMode: RenderMode.Prerender, // ビルド時に静的生成
},
{
path: 'products',
renderMode: RenderMode.Prerender,
},
{
path: 'products/:id',
renderMode: RenderMode.Server, // 動的 SSR
},
{
path: 'dashboard',
renderMode: RenderMode.Client, // クライアントのみ (SPA)
},
{
path: '**',
renderMode: RenderMode.Server,
},
];
レンダーモード
| モード | 説明 | ユースケース |
|---|---|---|
RenderMode.Prerender | ビルド時に静的 HTML を生成 | マーケティングページ、ブログ |
RenderMode.Server | リクエスト毎に動的 SSR | ユーザー固有のコンテンツ |
RenderMode.Client | クライアント側のみ (SPA) | 認証が必要なダッシュボード |
ハイドレーション
デフォルトハイドレーション
ハイドレーションは provideClientHydration() で デフォルト有効です:
// app.config.ts
import { provideClientHydration } from '@angular/platform-browser';
export const appConfig: ApplicationConfig = {
providers: [
provideClientHydration(),
// ...
],
};
インクリメンタルハイドレーション
特定のコンポーネントのハイドレーションを遅延させます:
@Component({
template: `
<!-- ビューポート内に入ったときにハイドレーション -->
@defer (hydrate on viewport) {
<app-comments [postId]="postId" />
} @placeholder {
<div class="comments-placeholder">Loading comments...</div>
}
<!-- ユーザー操作時にハイドレーション -->
@defer (hydrate on interaction) {
<app-interactive-chart [data]="chartData" />
}
<!-- ブラウザがアイドル時にハイドレーション -->
@defer (hydrate on idle) {
<app-recommendations />
}
<!-- ハイドレーションしない (静的のみ) -->
@defer (hydrate never) {
<app-static-footer />
}
`,
})
export class Post {
postId = input.required<string>();
chartData = input.required<ChartData>();
}
ハイドレーショントリガー
| トリガー | 説明 |
|---|---|
hydrate on viewport | 要素がビューポート内に入ったとき |
hydrate on interaction | クリック、フォーカス、入力時 |
hydrate on idle | ブラウザがアイドル時 |
hydrate on immediate | ロード直後 |
hydrate on timer(ms) | 指定遅延後 |
hydrate when condition | 式が true のとき |
hydrate never | ハイドレーションしない (静的) |
イベントリプレイ
ハイドレーション完了前にユーザーイベントをキャプチャします:
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
export const appConfig: ApplicationConfig = {
providers: [
provideClientHydration(withEventReplay()),
],
};
ブラウザ専用コード
プラットフォーム検出
import { PLATFORM_ID, inject } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
@Component({...})
export class My {
private platformId = inject(PLATFORM_ID);
ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
// ブラウザ専用コード
window.addEventListener('scroll', this.onScroll);
}
}
}
afterNextRender / afterRender
レンダリング後、ブラウザでのみコードを実行します:
import { afterNextRender, afterRender } from '@angular/core';
@Component({...})
export class Chart {
constructor() {
// 最初のレンダリング後に 1 回実行 (ブラウザのみ)
afterNextRender(() => {
this.initChart();
});
// すべてのレンダリング後に実行 (ブラウザのみ)
afterRender(() => {
this.updateChart();
});
}
private initChart() {
// ここでは DOM API を安全に使用できます
const canvas = document.getElementById('chart');
new Chart(canvas, this.config);
}
}
ブラウザ API を安全に注入
// tokens.ts
import { InjectionToken, PLATFORM_ID, inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
export const WINDOW = new InjectionToken<Window | null>('Window', {
providedIn: 'root',
factory: () => {
const platformId = inject(PLATFORM_ID);
return isPlatformBrowser(platformId) ? window : null;
},
});
export const LOCAL_STORAGE = new InjectionToken<Storage | null>('LocalStorage', {
providedIn: 'root',
factory: () => {
const platformId = inject(PLATFORM_ID);
return isPlatformBrowser(platformId) ? localStorage : null;
},
});
// 使用方法
@Injectable({ providedIn: 'root' })
export class Storage {
private storage = inject(LOCAL_STORAGE);
get(key: string): string | null {
return this.storage?.getItem(key) ?? null;
}
set(key: string, value: string): void {
this.storage?.setItem(key, value);
}
}
プリレンダリング
静的ルート
// app.routes.server.ts
export const serverRoutes: ServerRoute[] = [
{ path: '', renderMode: RenderMode.Prerender },
{ path: 'about', renderMode: RenderMode.Prerender },
{ path: 'contact', renderMode: RenderMode.Prerender },
{ path: 'blog', renderMode: RenderMode.Prerender },
];
getPrerenderParams を使った動的ルート
// app.routes.server.ts
import { RenderMode, ServerRoute, PrerenderFallback } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
{
path: 'products/:id',
renderMode: RenderMode.Prerender,
async getPrerenderParams() {
// プリレンダリングする商品 ID を取得
const response = await fetch('https://api.example.com/products');
const products = await response.json();
return products.map((p: Product) => ({ id: p.id }));
},
fallback: PrerenderFallback.Server, // 非プリレンダリングは SSR
},
{
path: 'blog/:slug',
renderMode: RenderMode.Prerender,
async getPrerenderParams() {
const posts = await fetchBlogPosts();
return posts.map(post => ({ slug: post.slug }));
},
fallback: PrerenderFallback.Client, // 非プリレンダリングは SPA
},
];
プリレンダーフォールバックオプション
| フォールバック | 説明 |
|---|---|
PrerenderFallback.Server | 非プリレンダリングルートは SSR |
PrerenderFallback.Client | クライアント側レンダリング |
PrerenderFallback.None | 非プリレンダリングルートは 404 |
HTTP キャッシング
TransferState
HTTP レスポンスをサーバーからクライアントに自動転送します:
import { provideClientHydration, withHttpTransferCacheOptions } from '@angular/platform-browser';
export const appConfig: ApplicationConfig = {
providers: [
provideClientHydration(
withHttpTransferCacheOptions({
includePostRequests: true,
includeRequestsWithAuthHeaders: false,
filter: (req) => !req.url.includes('/api/realtime'),
})
),
],
};
手動 TransferState
import { TransferState, makeStateKey } from '@angular/core';
const PRODUCTS_KEY = makeStateKey<Product[]>('products');
@Injectable({ providedIn: 'root' })
export class Product {
private http = inject(HttpClient);
private transferState = inject(TransferState);
private platformId = inject(PLATFORM_ID);
getProducts(): Observable<Product[]> {
// サーバーから転送されたデータをチェック
if (this.transferState.hasKey(PRODUCTS_KEY)) {
const products = this.transferState.get(PRODUCTS_KEY, []);
this.transferState.remove(PRODUCTS_KEY);
return of(products);
}
return this.http.get<Product[]>('/api/products').pipe(
tap(products => {
// サーバーで転送するために保存
if (isPlatformServer(this.platformId)) {
this.transferState.set(PRODUCTS_KEY, products);
}
})
);
}
}
ビルドとデプロイ
ビルドコマンド
# SSR でビルド
ng build
# 出力構造
dist/
├── my-app/
│ ├── browser/ # クライアント資産
│ └── server/ # サーバーバンドル
SSR サーバーを実行
# 開発環境
npm run serve:ssr:my-app
# 本番環境
node dist/my-app/server/server.mjs
Node.js ホストにデプロイ
// server.ts (生成されたもの)
import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr/node';
import express from 'express';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import bootstrap from './src/main.server';
const serverDistFolder = dirname(fileURLToPath(import.meta.url));
const browserDistFolder = resolve(serverDistFolder, '../browser');
const indexHtml = join(serverDistFolder, 'index.server.html');
const app = express();
const commonEngine = new CommonEngine();
app.get('*', express.static(browserDistFolder, { maxAge: '1y', index: false }));
app.get('*', (req, res, next) => {
commonEngine
.render({
bootstrap,
documentFilePath: indexHtml,
url: req.originalUrl,
publicPath: browserDistFolder,
providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }],
})
.then((html) => res.send(html))
.catch((err) => next(err));
});
app.listen(4000, () => {
console.log('Server listening on http://localhost:4000');
});
詳細なパターンについては、references/ssr-patterns.md を参照してください。
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- analogjs
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/analogjs/angular-skills / ライセンス: MIT
関連スキル
superfluid
Superfluidプロトコルおよびそのエコシステムに関するナレッジベースです。Superfluidについて情報を検索する際は、ウェブ検索の前にこちらを参照してください。対応キーワード:Superfluid、CFA、GDA、Super App、Super Token、stream、flow rate、real-time balance、pool(member/distributor)、IDA、sentinels、liquidation、TOGA、@sfpro/sdk、semantic money、yellowpaper、whitepaper
civ-finish-quotes
実質的なタスクが真に完了した際に、文明風の儀式的な引用句を追加します。ユーザーやエージェントが機能追加、リファクタリング、分析、設計ドキュメント、プロセス改善、レポート、執筆タスクといった実際の成果物を完成させるときに、明示的な依頼がなくても使用します。短い返信や小さな修正、未完成の作業には適用しません。
nookplot
Base(Ethereum L2)上のAIエージェント向け分散型調整ネットワークです。エージェントがオンチェーンアイデンティティを登録する、コンテンツを公開する、他のエージェントにメッセージを送る、マーケットプレイスで専門家を雇う、バウンティを投稿・請求する、レピュテーションを構築する、共有プロジェクトで協業する、リサーチチャレンジを解くことでNOOKをマイニングする、キュレーションされたナレッジを備えたスタンドアロンオンチェーンエージェントをデプロイする、またはアグリーメントとリワードで収益を得る場合に利用できます。エージェントネットワーク、エージェント調整、分散型エージェント、NOOKトークン、マイニングチャレンジ、ナレッジバンドル、エージェントレピュテーション、エージェントマーケットプレイス、ERC-2771メタトランザクション、Prepare-Sign-Relay、AgentFactory、またはNookplotが言及された場合にトリガーされます。
web3-polymarket
Polygon上でのPolymarket予測市場取引統合です。認証機能(L1 EIP-712、L2 HMAC-SHA256、ビルダーヘッダー)、注文発注(GTC/GTD/FOK/FAK、バッチ、ポストオンリー、ハートビート)、市場データ(Gamma API、Data API、オーダーブック、サブグラフ)、WebSocketストリーミング(市場・ユーザー・スポーツチャネル)、CTF操作(分割、統合、償却、ネガティブリスク)、ブリッジ機能(入金、出金、マルチチェーン)、およびガスレスリレイトランザクションに対応しています。AIエージェント、自動マーケットメーカー、予測市場UI、またはPolygraph上のPolymarketと統合するアプリケーション構築時に活用できます。
ethskills
Ethereum、EVM、またはブロックチェーン関連のリクエストに対応します。スマートコントラクト、dApps、ウォレット、DeFiプロトコルの構築、監査、デプロイ、インタラクションに適用されます。Solidityの開発、コントラクトアドレス、トークン規格(ERC-20、ERC-721、ERC-4626など)、Layer 2ネットワーク(Base、Arbitrum、Optimism、zkSync、Polygon)、Uniswap、Aave、Curveなどのプロトコルとの統合をカバーします。ガスコスト、コントラクトのデシマル設定、オラクルセキュリティ、リエントランシー、MEV、ブリッジング、ウォレット管理、オンチェーンデータの取得、本番環境へのデプロイ、プロトコル進化(EIPライフサイクル、フォーク追跡、今後の変更予定)といったトピックを含みます。
xxyy-trade
このスキルは、ユーザーが「トークン購入」「トークン売却」「トークンスワップ」「暗号資産取引」「取引ステータス確認」「トランザクション照会」「トークンスキャン」「フィード」「チェーン監視」「トークン照会」「トークン詳細」「トークン安全性確認」「ウォレット一覧表示」「マイウォレット」「AIスキャン」「自動スキャン」「ツイートスキャン」「オンボーディング」「IP確認」「IPホワイトリスト」「トークン発行」「自動売却」「損切り」「利益確定」「トレーリングストップ」「保有者」「トップホルダー」「KOLホルダー」などをリクエストした場合、またはSolana/ETH/BSC/BaseチェーンでXXYYを経由した取引について言及した場合に使用します。XXYY Open APIを通じてオンチェーン取引とデータ照会を実現します。