angular-ssr

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Angular SSR - Quick Reference

Angular SSR - 快速参考

Deep Knowledge: Use
mcp__documentation__fetch_docs
with technology:
angular
, topic:
ssr
for comprehensive documentation.
深度文档:使用
mcp__documentation__fetch_docs
工具,指定technology为
angular
、topic为
ssr
可获取完整文档。

Setup

配置

bash
undefined
bash
undefined

Add SSR to existing project

为现有项目添加SSR

ng add @angular/ssr

This creates:
- `server.ts` - Express server entry point
- `src/app/app.config.server.ts` - Server-specific providers
- Updates `angular.json` with server builder
ng add @angular/ssr

此命令会创建:
- `server.ts` - Express服务器入口文件
- `src/app/app.config.server.ts` - 服务器专属提供者配置
- 更新`angular.json`中的服务器构建器配置

Server Configuration

服务器配置

typescript
// app.config.server.ts
import { mergeApplicationConfig, ApplicationConfig } 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);
typescript
// app.config.server.ts
import { mergeApplicationConfig, ApplicationConfig } 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);

Route-Level Rendering Modes

路由级渲染模式

typescript
// app.routes.server.ts
import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  { path: '', renderMode: RenderMode.Prerender },           // Static at build time
  { path: 'dashboard', renderMode: RenderMode.Server },      // SSR per request
  { path: 'profile/**', renderMode: RenderMode.Client },     // Client-only (SPA)
  { path: '**', renderMode: RenderMode.Server },              // Default: SSR
];
typescript
// app.routes.server.ts
import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  { path: '', renderMode: RenderMode.Prerender },           // 构建时静态预渲染
  { path: 'dashboard', renderMode: RenderMode.Server },      // 每次请求触发SSR
  { path: 'profile/**', renderMode: RenderMode.Client },     // 仅客户端渲染(SPA模式)
  { path: '**', renderMode: RenderMode.Server },              // 默认:SSR
];

Hydration

Hydration(水合)

typescript
// app.config.ts - Hydration is enabled by default with @angular/ssr
export const appConfig: ApplicationConfig = {
  providers: [
    provideClientHydration(),  // Included automatically
  ]
};
typescript
// app.config.ts - 使用@angular/ssr时默认启用Hydration
export const appConfig: ApplicationConfig = {
  providers: [
    provideClientHydration(),  // 自动包含
  ]
};

Platform Checks

平台检测

typescript
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { PLATFORM_ID, inject } from '@angular/core';

@Component({ ... })
export class MyComponent {
  private platformId = inject(PLATFORM_ID);

  ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
      // Browser-only code (localStorage, window, DOM manipulation)
      window.addEventListener('scroll', this.onScroll);
    }
  }
}

// Or use afterNextRender for one-time browser init
import { afterNextRender } from '@angular/core';

@Component({ ... })
export class ChartComponent {
  constructor() {
    afterNextRender(() => {
      // Runs only in browser after first render
      this.initChart();
    });
  }
}
typescript
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { PLATFORM_ID, inject } from '@angular/core';

@Component({ ... })
export class MyComponent {
  private platformId = inject(PLATFORM_ID);

  ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
      // 仅浏览器端执行的代码(localStorage、window、DOM操作)
      window.addEventListener('scroll', this.onScroll);
    }
  }
}

// 或使用afterNextRender进行一次性浏览器初始化
import { afterNextRender } from '@angular/core';

@Component({ ... })
export class ChartComponent {
  constructor() {
    afterNextRender(() => {
      // 仅在浏览器端首次渲染后执行
      this.initChart();
    });
  }
}

Anti-Patterns

反模式

Anti-PatternWhy It's BadCorrect Approach
Direct
window
/
document
access
Breaks SSRUse
isPlatformBrowser()
No hydrationFull page re-renderEnable
provideClientHydration()
SSR for auth-only pagesWasted server resourcesUse
RenderMode.Client
Ignoring transfer stateDouble data fetchHydration handles this automatically
反模式危害正确做法
直接访问
window
/
document
破坏SSR渲染使用
isPlatformBrowser()
进行判断
未启用Hydration页面完全重渲染启用
provideClientHydration()
对仅需授权的页面使用SSR浪费服务器资源使用
RenderMode.Client
忽略状态传递重复数据请求Hydration会自动处理此问题

Quick Troubleshooting

快速故障排除

IssueLikely CauseSolution
Hydration mismatchDOM changed before hydrationAvoid DOM manipulation in
ngOnInit
window is not defined
Server-side accessGuard with
isPlatformBrowser()
Slow SSRHeavy computationMove to client with
@defer
SEO not workingClient-only renderingUse
RenderMode.Server
or
Prerender
问题可能原因解决方案
Hydration不匹配水合前DOM已被修改避免在
ngOnInit
中操作DOM
window is not defined
在服务器端访问了window对象使用
isPlatformBrowser()
进行防护
SSR速度慢存在大量计算逻辑使用
@defer
将逻辑移至客户端
SEO不生效使用了仅客户端渲染使用
RenderMode.Server
Prerender
模式