
Angular 17 原生支持静态预渲染(SSG),通过 @angular/ssr 包在构建时生成真实 HTML 页面;但若路由配置不匹配或数据未在服务端解析,预渲染内容将仅含骨架结构,实际数据仍由客户端 JavaScript 补全。
angular 17 原生支持静态预渲染(ssg),通过 `@angular/ssr` 包在构建时生成真实 html 页面;但若路由配置不匹配或数据未在服务端解析,预渲染内容将仅含骨架结构,实际数据仍由客户端 javascript 补全。
Angular 17 对预渲染(Prerendering)进行了重大升级——它不再依赖 Angular Universal 的传统 Express 服务器配置,而是引入了轻量、现代化的 @angular/ssr 包,作为官方推荐的 SSR/SSG 统一解决方案。该方案默认启用静态站点生成(Static Site Generation, SSG),即在构建阶段(build time)为指定路由预先执行组件渲染,并输出包含完整 DOM 结构和初始数据的静态 HTML 文件,显著提升首屏加载性能与 SEO 友好性。
然而,正如你在 dist/ssr/browser/product/bronco/index.html 中观察到的现象:HTML 文件中 <app-root></app-root> 内部为空,仅保留占位结构(如 <router-outlet></router-outlet> 和 <!--nghm--> 注释),且 <script id="ng-state"></script> 中的 JSON 数据为空({"__nghData__":[{"c":{"0":[]}}]}),这并非构建失败,而是典型的服务端数据未注入表现。根本原因往往在于:
✅ 路由路径不匹配(最常见)
你预渲染的路径是 /product/bronco,但 Angular 路由器实际配置的是 product/:id(单数)还是 products/:id(复数)?若 RouterModule.forRoot() 中定义的是 { path: 'products/:id', component: ProductDetailComponent },而你却在 prerender 配置中声明 routes: ['/product/bronco'],则 Angular 无法匹配该路由,导致对应组件不会被实例化,更不会执行其 resolve 或 ngOnInit 中的数据获取逻辑——最终只渲染空壳。
✅ 数据获取未在服务端执行
Angular 17 的预渲染默认不会自动执行组件内的异步数据获取(如 HttpClient.get())。你必须显式使用 resolve 守卫(ResolveFn)或 defer + async 等服务端感知机制,确保数据在渲染前就绪。例如:
// product-resolver.ts
import { inject } from '@angular/core';
import { ResolveFn } from '@angular/router';
import { HttpClient } from '@angular/common/http';
export const productResolver: ResolveFn<product> = (route) => {
const http = inject(HttpClient);
const id = route.paramMap.get('id');
return http.get<product>(`/api/products/${id}`);
};</product></product>
并在路由中注册:
{
path: 'product/:id',
component: ProductDetailComponent,
resolve: { product: productResolver }
}
✅ ng-state 为空说明服务端未序列化数据<script id="ng-state"></script> 是 Angular 水合(Hydration)的关键载体,用于将服务端计算出的状态传递给客户端。若其中无有效数据,表明 productResolver 未被执行,或执行时抛出错误(需检查构建日志中的 prerender 步骤输出),亦或 ProductDetailComponent 未正确使用 inject(Product) 或 @Input() 接收解析结果。
? 如何验证预渲染是否成功?
- 构建后检查
dist/ssr/browser/product/bronco/index.html是否包含实际产品信息(如<h1>Bronco SUV</h1>); - 在无 JS 环境下打开该 HTML(如禁用浏览器 JS 后刷新),确认内容可见;
- 运行
npx serve -s dist/ssr/browser本地托管并访问/product/bronco,观察 Network 面板中是否跳过main-*.js的初始数据请求。
⚠️ 注意事项
- Angular 17 的
prerender是构建时行为,不依赖运行时 Node.js 服务器(区别于传统 SSR); - 所有预渲染路由必须在
angular.json的prerender.options.routes中显式声明,不支持通配符或动态路由自动发现; - 使用
OnPush变更检测策略的组件,在预渲染中需确保输入绑定的数据已由resolve提供,否则视图不会更新; - 避免在
ngOnInit中直接调用HttpClient——它在构建时不可用,应全部迁移至resolve或loadChildren的预加载逻辑中。
总之,Angular 17 的预渲染不是“一键魔法”,而是要求开发者明确声明路由、显式注入数据、并验证服务端执行路径。一旦配置准确,你将获得真正静态、SEO 就绪、零水合延迟的 HTML 页面——这才是现代 Angular 应用性能与体验的基石。










