使用Angular Universal在Angular中执行服务器端渲染时,无法在页面重定向后查看我的html源代码



我正在开发一个Angular Blog应用程序,我已经在我的博客应用程序中实现了服务器端渲染。我遇到的问题是,在我的博客主页上,我可以通过点击来自Node.js API的Ctrl+u来查看所有博客及其源代码内容,单击选定的博客后,它将重定向到该博客并显示其内容,但不显示HTML源代码(它存储在本地存储中,用于减少API调用,我从本地存储中获取它以显示选定的博客内容(。

我还读到SSR不支持窗口、文档和位置对象,而本地存储是一个窗口属性,所以为了使用它,我必须遵循以下链接https://stackoverflow.com/a/57781883.

在完成了所有的教程和文档后,我尝试了所有可能的方法,但都没有成功。我们将不胜感激。提前谢谢。

下面是我的代码附加

主页组件.html

<owl-carousel-o [options]="bannerSlider">
<ng-template carouselSlide>
<div class="sliderblogbox">
<img class="penci-image-holder" src="{{recentPost[0]?.featured_image}}">
<div class="sliderblogbox-text">
<h3><a (click)="showDetails(recentPost[0])">{{recentPost[0]?.title}}</a></h3> <span
class="daytime">{{recentPost[0]?.post_date * 1000 | date:'mediumDate'}}</span>
</div>
</div>
</ng-template>
<ng-template carouselSlide>
<div class="sliderblogbox">
<img class=" penci-image-holder" src={{recentPost[1]?.featured_image}}>
<div class="sliderblogbox-text">
<h3><a (click)="showDetails(recentPost[1])">{{recentPost[1]?.title}}</a></h3> <span
class="daytime">{{recentPost[1]?.post_date * 1000 | date:'mediumDate'}}</span>
</div>
</div>
</ng-template>
<ng-template carouselSlide>
<div class="sliderblogbox">
<img class=" penci-image-holder" src={{recentPost[0]?.featured_image}}>
<div class="sliderblogbox-text">
<h3><a (click)="showDetails(recentPost[0])">{{recentPost[0]?.title}}</a></h3> <span
class="daytime">{{recentPost[0]?.post_date * 1000 | date:'mediumDate'}}</span>
</div>
</div>
</ng-template>
<ng-template carouselSlide>
<div class="sliderblogbox">
<img class=" penci-image-holder" src={{recentPost[1]?.featured_image}}>
<div class="sliderblogbox-text">
<h3><a (click)="showDetails(recentPost[1])">{{recentPost[1]?.title}}</a></h3> <span
class="daytime">{{recentPost[1]?.post_date * 1000 | date:'mediumDate'}}</span>
</div>
</div>
</ng-template>
</owl-carousel-o>

主页组件.ts

bannerSlider: OwlOptions = {
loop: false,
navText: [''],
nav: true,
margin: 10,
dots: false,
responsive: {
0: {
items: 1
},
600: {
items: 2
},
1000: {
items: 2
}
}
};
relatedPost: OwlOptions = {
loop: true,
navText: [''],
nav: true,
margin: 15,
dots: false,
responsive: {
0: {
items: 1
},
600: {
items: 2
},
1000: {
items: 3
}
}
};
constructor(private _service: ServiceService, private router: Router) { }
recentPost = [];
ngOnInit(): void {
this.getRecentPost()
}
getRecentPost() {
let key = 'slider';
this._service.getRecentPostSlider(key).subscribe(res => {
if (!res.error) {
this.recentPost = res.data.data;
}
})
}
showDetails(post) {
localStorage.setItem('post', JSON.stringify(post));
this.router.navigate([`/${post.title_abbr}/`]);
}

BlogPost组件.html

<div class="largeblog-area">
<div class="largeblog-content blogdetailtop">
<h6><a href=""> {{post?.category}} </a> </h6>
<h2>{{post?.title}}</h2>
<h5><span>written by {{post?.author}} </span> |
<span>{{post?.post_date * 1000 | date:'mediumDate'}}</span> </h5>
</div>

<div class="largeblogimg"><img alt="img" src="{{post?.featured_image}}"></div>
<div class="largeblog-content">
<p [innerHTML]="post?.description"></p>
<div class="post-tags">
<a href="#">fresh</a>
<a href="#">life</a>
<a href="#">style</a>
<a href="#">travel</a>
</div>

</div>
<div class="largeblog-bottombox">
<div class="largeblog-date">
<span><i class="fa fa-clock-o" aria-hidden="true"></i> July 5, 2017</span>
</div>
<div class="largeblog-social">
<ul>
<li><a href="" title="Like"><i class="fa fa-heart-o" aria-hidden="true"></i></a>
</li>
<li><a href="" title="Facebook"><i class="fa fa-facebook"
aria-hidden="true"></i></a> </li>
<li><a href="" title="Twitter"><i class="fa fa-twitter" aria-hidden="true"></i></a>
</li>
<li><a href="" title="Pinterest"><i class="fa fa-pinterest"
aria-hidden="true"></i></a> </li>
</ul>
</div>

</div>
</div>

BlogPost组件.ts

post: any;
constructor(private meta: Meta, private router: Router, private titleService: Title,
private globle: Globals, private _interaction: CommunicationService, private route: ActivatedRoute) {
this.post = JSON.parse(localStorage.getItem('post'));
if (this.post && this.post.meta_description !== '') {
this.meta.addTag({ name: 'title:', content: this.post.meta_description });
}
}
ngOnInit(): void {
console.log(this.post);
this.router.events.subscribe(event => {
if (event instanceof NavigationEnd) {
this.post = JSON.parse(localStorage.getItem('post'));
}
});
}

只有当您的express服务器没有崩溃时,SSR才会显示视图源。SSR不支持浏览器中提供的窗口和文档对象

如果您使用服务工作者进行缓存,即使这样,它也不会显示视图源,因为它将从服务工作者提供服务。

因此,您必须在两个地方进行检查,如果它是否由服务人员提供服务,然后检查您的服务器是否在终端中显示生成的HTML。

使用routeReuseStrategy在导航后获取查看源上的数据,或使用解析器查看查看源上的更新内容

解析器

数据提供程序类可以与路由器一起使用,以在导航期间解析数据。该接口定义了一个resolve((方法,该方法在ResolveStart路由器事件之后立即调用。在路由最终被激活之前,路由器等待数据被解析。

它的工作原理

在基于组件/体系结构的内部创建一个解析器。在General-Landing-Component.ts中,订阅解析器函数并调用API调用来查看SSR上的页面。

通用着陆解析程序

import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
import { ActivatedRouteSnapshot,  Resolve,  RouterStateSnapshot,  Router,  ActivatedRoute,} from '@angular/router';
import { Observable, of } from 'rxjs';
import { ContentfulService } from '../../contentful/services/contentful.service';
import { ContentfulResponse } from '../../contentful/interfaces/contentful-response';
import { map } from 'rxjs/operators';
import { isPlatformBrowser } from '@angular/common';
@Injectable({
providedIn: 'root',
})
export class generalLandingResolver implements Resolve<ContentfulResponse> {
urlPath: any;
responseData: any;
private isBrowser: boolean = false;
constructor(
private contentfulService: ContentfulService,
private router: Router,
private activatedRoute: ActivatedRoute,
@Inject(PLATFORM_ID) private platformId: Object
) {
this.isBrowser = isPlatformBrowser(platformId);
}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any> {
const tree = this.router.parseUrl(state.url);
const children = tree.root.children.primary;
const segments = children.segments[1].path;
this.urlPath = segments;
return this.contentfulService.generalLandingPageBySlug(this.urlPath).pipe(
map((response) => {
if (this.isBrowser) {
sessionStorage.setItem('response', response);
}
})
);
}
}

通用着陆组件。ts

import { Component, OnInit, Inject, PLATFORM_ID } from '@angular/core';
import { Router } from '@angular/router';
import { combineLatest, EMPTY, Observable } from 'rxjs';
import { ContentfulResponse } from '../../contentful/interfaces/contentful-response';
import { ContentfulService } from '../../contentful/services/contentful.service';
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
@Component({
selector: 'app-general-landing-page',
templateUrl: './general-landing-page.component.html',
styleUrls: ['./general-landing-page.component.scss'],
})
export class GeneralLandingPageComponent implements OnInit {
contentfulResponse$!: Observable<ContentfulResponse>;
componentschoosen: any;
componentsResults: any;
urlPath: any;
siteURL: any;
siteURLpath: any;
private isBrowser: boolean = false;
constructor(
private contentfulService: ContentfulService,
private router: Router,
@Inject(DOCUMENT) private dom: Document,
@Inject(PLATFORM_ID) private platformId: Object
) {
this.isBrowser = isPlatformBrowser(platformId);
}
ngOnInit(): void {
this.siteURL = new URL(this.dom.URL);
this.siteURLpath = this.siteURL?.pathname;
this.urlPath = this.siteURLpath.split('/')[2];
this.contentfulService
.generalLandingPageBySlug(this.urlPath)
.subscribe((_) => {
if (this.contentfulService.generalLandingResponse == null) {
this.contentfulService.generalLandingResponse = sessionStorage.getItem(
'response'
);
}
if (
this.contentfulService.generalLandingResponse.includes(
'__typename ...'
)
) {
this.contentfulResponse$ = this.contentfulService.generalLandingPageProcessedData(
this.contentfulService.generalLandingResponse,
this.urlPath
);
}
});
this.router.routeReuseStrategy.shouldReuseRoute = function () {
return false;
};
}
ngAfterContentChecked(): void {
if (this.isBrowser) {
sessionStorage.removeItem('response');
}
}
}

导入解析程序文件

通用着陆组件模块

import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CoreModule } from '../../core/core.module';
import { GeneralLandingPageComponent } from './general-landing-page.component';
import { generalLandingResolver } from './general-landing-page.resolver';
const routes: Routes = [
{
path: '',
component: GeneralLandingPageComponent,
resolve: { generalContent: generalLandingResolver },
},
];
@NgModule({
declarations: [GeneralLandingPageComponent],
imports: [CommonModule, CoreModule, [RouterModule.forChild(routes)]],
})
export class GeneralLandingPageModule {}

通用着陆组件.html

<ng-container *ngIf="contentfulResponse$ | async as data">
<ng-container *ngFor="let component of data?.components">
<app-contentful
[template]="component.pageData.__typename"
[component]="component.pageData"
></app-contentful>
</ng-container>
</ng-container>

最新更新