Angular预渲染下如何仅服务端调用API并使数据显示在页面源码
解决Angular预渲染博客模块的API调用与数据共享问题
要实现仅在服务端调用API,同时保证浏览器端能复用预渲染数据且数据出现在页面源码中,核心是利用Angular的TransferState机制在服务端与浏览器端共享数据,避免重复请求并保留预渲染数据。
步骤1:配置模块依赖
在服务端模块(如app.server.module.ts)中导入ServerTransferStateModule,在浏览器模块(如app.module.ts)中导入BrowserTransferStateModule:
// app.server.module.ts import { ServerTransferStateModule } from '@angular/platform-server'; @NgModule({ imports: [ // ...其他导入 ServerTransferStateModule ] }) export class AppServerModule {}
// app.module.ts import { BrowserTransferStateModule } from '@angular/platform-browser'; @NgModule({ imports: [ // ...其他导入 BrowserTransferStateModule ] }) export class AppModule {}
步骤2:组件中实现数据逻辑
在博客列表组件中,通过TransferState存储服务端获取的数据,浏览器端直接读取该数据而非重新请求API:
import { Component, OnInit, Inject, PLATFORM_ID } from '@angular/core'; import { isPlatformServer, isPlatformBrowser } from '@angular/common'; import { TransferState, makeStateKey } from '@angular/platform-browser'; import { HttpClient } from '@angular/common/http'; import { Observable, of } from 'rxjs'; import { tap } from 'rxjs/operators'; // 定义唯一的状态键,用于标识存储的博客数据 const BLOG_PAGE_INFO_KEY = makeStateKey<any>('blog-page-info'); @Component({ selector: 'app-blog-list', templateUrl: './blog-list.component.html' }) export class BlogListComponent implements OnInit { blogData$: Observable<any>; constructor( @Inject(PLATFORM_ID) private platformId: object, private transferState: TransferState, private http: HttpClient ) {} ngOnInit(): void { this.getBlogPageInfo(); } private getBlogPageInfo(): void { if (isPlatformBrowser(this.platformId)) { // 浏览器端优先读取TransferState中的预渲染数据 const savedData = this.transferState.get(BLOG_PAGE_INFO_KEY, null); // 若预渲染未存入数据(极端情况),才发起API请求 this.blogData$ = savedData ? of(savedData) : this.fetchBlogData(); // 读取后清除状态,避免后续重复读取(可选) this.transferState.remove(BLOG_PAGE_INFO_KEY); } else if (isPlatformServer(this.platformId)) { // 服务端调用API,并存入TransferState供浏览器端复用 this.blogData$ = this.fetchBlogData().pipe( tap(data => this.transferState.set(BLOG_PAGE_INFO_KEY, data)) ); } } // 封装API调用逻辑 private fetchBlogData(): Observable<any> { return this.http.get('/api/blog/page-info'); } }
步骤3:模板中使用数据
在组件模板中通过async管道订阅数据,确保预渲染时数据能被正确渲染到HTML源码中:
<div *ngFor="let blog of blogData$ | async"> <h3>{{blog.title}}</h3> <p>{{blog.summary}}</p> </div>
原理说明
- 预渲染阶段:服务端执行组件代码,调用API获取数据并通过
TransferState存入,Angular会自动将数据序列化为页面中的<script>标签(格式为window.__TRANSFER_STATE__),最终生成包含数据的静态HTML。 - 浏览器加载阶段:组件初始化时直接从
TransferState读取预渲染数据,无需重复发起API请求,同时数据已包含在页面源码中,满足SEO需求。
内容的提问来源于stack exchange,提问作者Mhd Adil Khan
相关产品推荐
相关产品推荐

