[Angular] Angular Server-Side Rendering with Angular CLI 1.3.x 基本設定

Angular CLI 1.3.0 和 Angular 4.3 版本之前,要使用 SSR (Server Side Rendering) 其實還蠻複雜的,並不是無法做到,只是很麻煩。可是,當版本升級到 Angular 4.3 及 CLI 1.3.0 以後,這一切都變得非常簡單。這裡先說明如何快速地將 SSR 的環境建置起來

前置作業

  1. 先將 CLI 的版本升級到 1.3.0。可以從這看到目前的釋出版本 CLI Release (2017/08/08 時,CLI版本是 1.3.0-rc.5)

    npm install @angular/[email protected]

  2. 安裝 @angular/platform-server

    npm install @angular/platform-server

設定步驟

.angular-cli.json

多新增一個 app 設定

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "universal",
"platform": "server",
"root": "src",
"outDir": "dist-server",
"main": "main-server.ts",
"tsconfig": "tsconfig.server.json",
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
  • 刪除 polyfiils,伺服器不需要這個

  • 移除 assetsstylesscripts ,這部分已經在 browser 版本中處理過了。

main-server.ts

scr 資料夾下新增 main-server.ts 檔案

1
2
3
4
import {enableProdMode} from '@angular/core';
export {AppServerModule} from './app/app-server.module';

enableProdMode();

app-server.module

src/app 資料夾下,新增 `app-server.module.ts’ 檔案

1
2
3
4
5
6
7
8
9
10
import {NgModule} from '@angular/core';
import {ServerModule} from '@angular/platform-server';

import {AppComponent} from './app.component';
import {AppModule} from './app.module';

@NgModule({imports: [AppModule, ServerModule], bootstrap: [AppComponent]})
export class AppServerModule {
}

app.module

修改 app.module.ts 檔案,給予 withServerTransition 的設定值,appId 可以任取。

1
2
3
4
5
6
...
imports: [
BrowserModule.withServerTransition({appId: 'universal'}),
...
],
...

tsconfig.server.json

src 資料夾下,新增 tsconfig.server.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "../out-tsc/server",
/* For now, Node only understand CommonJS modules, so you can't stick to es2015 here */
"module": "commonjs"
},
"exclude": [
"test.ts",
"**/*.spec.ts"
],
/* Additional informations to bootstrap Angular */
"angularCompilerOptions": {
"entryModule": "app/app-server.module#AppServerModule"
}
}

package.json

修改 scripts 指令

「build」: 「ng build --prod && ng build --prod --app universal --output-hashing=none」

這樣子當執行 npm run build 時,就會分別建置瀏覽器端及伺服器端所需要的程式了

設定 node express server

在專案跟目錄下新增 server.js 檔案,這檔案的功能是執行一個 node express 網站伺服器

套件安裝

npm install express @nguniversal/express-engine

server.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
'use strict';

/* Server specific version of Zone.js */
require('zone.js/dist/zone-node');

const express = require('express');
const ngUniversal = require('@nguniversal/express-engine');

/* The server bundle is loaded here, it's why you don't want a changing hash in
* it */
const appServer = require('./dist-server/main.bundle');

/* Server-side rendering */
function angularRouter(req, res) {
/* Server-side rendering */
res.render('index', {req, res});
}

const app = express();

/* Root route before static files, or it will serve a static index.html, without
* pre-rendering */
app.get('/', angularRouter);

/* Serve the static files generated by the CLI (index.html, CSS? JS, assets...)
*/
app.use(express.static(`${__dirname}/dist`));

/* Configure Angular Express engine */
app.engine('html', ngUniversal.ngExpressEngine({
bootstrap: appServer.AppServerModuleNgFactory
}));
app.set('view engine', 'html');
app.set('views', 'dist');

/* Direct all routes to index.html, where Angular will take care of routing */
app.get('*', angularRouter);

app.listen(3000, () => {
console.log(`Listening on http://localhost:3000`);
});

執行方式

node server.js

執行上述指令後,就會將一個網站伺服器跑起來,而開啟 http://localhost:3000 的網站,即可看到由伺服器產生的 Angular 網站

參考資料