详解Angular5 服务端渲染实战(3)
为了打包方便最好在 package.json 里面加几行脚本,如下:
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "run-s build:client build:aot build:server",
"build:client": "ng build -prod --build-optimizer --app 0",
"build:aot": "ng build --aot --app 1",
"build:server": "webpack -p",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
}
现在尝试运行 npm run build ,将会看到如下输出:
node 运行刚刚打包好的 node dist/server.js 文件
打开 http://localhost:4200/ 会正常显示项目主页面
从上面的开发者工具可以看出 html 文档是服务端渲染直出的,接下来尝试请求数据试一下。
注意:本项目显式(菜单可点击)的几个路由初始化都没有请求数据,但是单词解释的详情页是会在 ngOnInit() 方法里获取数据,例如: http://localhost:4200/detail/add 直接打开时会发生奇怪的现象,请求在服务端和客户端分别发送一次,正常的服务端渲染项目首屏初始化数据的请求在服务端执行,在客户端不会二次请求!
发现问题后,就来踩平这个坑
试想如果采用一个标记来区分服务端是否已经拿到了数据,如果没拿到数据就在客户端请求,如果已经拿到数据就不发请求
当然 Angular 早有一手准备,那就是 Angular Modules for Transfer State
那么如何真实运用呢?见下文
请求填坑
在服务端入口和客户端入口分别引入 TransferStateModule
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
// ...
@NgModule({
imports: [
// ...
ServerModule,
ServerTransferStateModule
]
// ...
})
export class AppServerModule { }
import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser';
// ...
@NgModule({
declarations: [
AppComponent
// ...
],
imports: [
BrowserModule.withServerTransition({ appId: 'udao' }),
BrowserTransferStateModule
// ...
]
// ...
})
export class AppModule { }
以本项目为例在 detail.component.ts 里面,修改如下
import { Component, OnInit } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router'
import { TransferState, makeStateKey } from '@angular/platform-browser'
const DETAIL_KEY = makeStateKey('detail')
// ...
export class DetailComponent implements OnInit {
details: any
// some variable
constructor(
private http: HttpClient,
private state: TransferState,
private route: ActivatedRoute,
private router: Router
) {}
transData (res) {
// translate res data
}
ngOnInit () {
this.details = this.state.get(DETAIL_KEY, null as any)
if (!this.details) {
this.route.params.subscribe((params) => {
this.loading = true
const apiURL = `https://dict.youdao.com/jsonapi?q=${params['word']}`
this.http.get(`/?url=${encodeURIComponent(apiURL)}`)
.subscribe(res => {
this.transData(res)
this.state.set(DETAIL_KEY, res as any)
this.loading = false
})
})
} else {
this.transData(this.details)
}
}
}
内容版权声明:除非注明,否则皆为本站原创文章。
