ホームページ > 記事 > ウェブフロントエンド > Angular5 を使用してサーバー側レンダリングの実践を実装する
この記事では主に Angular5 のサーバーサイドレンダリングの実践について詳しく説明しますので、参考にしてください。
この記事は、以前の Angular5 の記事に基づいて開発を続けます。上記の記事では、Angular5 Youdao Translation を構築するプロセスと、発生した問題の解決策について説明します。
その後、UI が bootstrap4 から angular マテリアルに変更されました。ここでは、サーバー側のレンダリングは UI の変更とは関係ありません。
これまでの記事を読んでいただいた方は、記事の内容がサーバーサイドレンダリング、vueのnuxt、reactのnextに偏っていることに気づくと思います。
この改訂の前に、時間を大幅に節約できる nuxt.js や next.js などのトップレベルのパッケージ化ライブラリを見つけようとしましたが、役に立ちませんでした。
最終的に、Angular2 から利用可能なフロントエンドとバックエンドの同型ソリューションである Angular Universal (Universal (isomorphic) JavaScript support for Angular.) を使用することにしました
この記事でもドキュメントの内容については詳しく紹介しません。 Angular の SSR へのわかりやすい言語の使用を試みます
前提
以前に作成した udao プロジェクトは、構築からパッケージ化まで完全に angular-cli に準拠しており、この記事は angular5 で構築されたすべてのプロジェクトに共通になります。角度-cli。
構築プロセス
最初にサーバーの依存関係をインストールします
yarn add @angular/platform-server express yarn add -D ts-loader webpack-node-externals npm-run-all
ここで、@angular/platform-server のバージョン番号は、現在の angular バージョンに従ってインストールするのが最適であることに注意してください (例: @angular/platform-)他の依存関係とのバージョンの競合を避けるため、server@5.1.0。
ファイルの作成: src/app/app.server.module.ts
import { NgModule } from '@angular/core' import { ServerModule } from '@angular/platform-server' import { AppModule } from './app.module' import { AppComponent } from './app.component' @NgModule({ imports: [ AppModule, ServerModule ], bootstrap: [AppComponent], }) export class AppServerModule { }
ファイルの更新: src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser' import { NgModule } from '@angular/core' // ... import { AppComponent } from './app.component' // ... @NgModule({ declarations: [ AppComponent // ... ], imports: [ BrowserModule.withServerTransition({ appId: 'udao' }) // ... ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
サーバーモジュールをエクスポートするにはメインファイルが必要です
ファイルの作成: src /main.server.ts
export { AppServerModule } from './app/app.server.module'
それでは @angular/cli.angular-cli.json の設定ファイルを更新しましょう
{ "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "project": { "name": "udao" }, "apps": [ { "root": "src", "outDir": "dist/browser", "assets": [ "assets", "favicon.ico" ] // ... }, { "platform": "server", "root": "src", "outDir": "dist/server", "assets": [], "index": "index.html", "main": "main.server.ts", "test": "test.ts", "tsconfig": "tsconfig.server.json", "testTsconfig": "tsconfig.spec.json", "prefix": "app", "scripts": [], "environmentSource": "environments/environment.ts", "environments": { "dev": "environments/environment.ts", "prod": "environments/environment.prod.ts" } } ] // ... }
上記の // ... は省略することを意味しますが、json にはコメントがありません。奇妙な....
もちろん、.angular-cli.json の構成は固定されていません。必要に応じて変更できます
サーバーの tsconfig 構成ファイルを作成する必要があります: src/tsconfig.server。 json
{ "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/app", "baseUrl": "./", "module": "commonjs", "types": [] }, "exclude": [ "test.ts", "**/*.spec.ts", "server.ts" ], "angularCompilerOptions": { "entryModule": "app/app.server.module#AppServerModule" } }
次に更新します: src /tsconfig.app.json
{ "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/app", "baseUrl": "./", "module": "es2015", "types": [] }, "exclude": [ "test.ts", "**/*.spec.ts", "server.ts" ] }
これで、次のコマンドを実行して構成が有効かどうかを確認できます
ng build -prod --build-optimizer --app 0 ng build --aot --app 1
実行結果は下の図のようになります
次に、Express.js サービスを作成し、ファイルを作成します: src/server.ts
import 'reflect-metadata' import 'zone.js/dist/zone-node' import { renderModuleFactory } from '@angular/platform-server' import { enableProdMode } from '@angular/core' import * as express from 'express' import { join } from 'path' import { readFileSync } from 'fs' enableProdMode(); const PORT = process.env.PORT || 4200 const DIST_FOLDER = join(process.cwd(), 'dist') const app = express() const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString() const { AppServerModuleNgFactory } = require('main.server') app.engine('html', (_, options, callback) => { const opts = { document: template, url: options.req.url } renderModuleFactory(AppServerModuleNgFactory, opts) .then(html => callback(null, html)) }); app.set('view engine', 'html') app.set('views', 'src') app.get('*.*', express.static(join(DIST_FOLDER, 'browser'))) app.get('*', (req, res) => { res.render('index', { req }) }) app.listen(PORT, () => { console.log(`listening on http://localhost:${PORT}!`) })
もちろん、server.ts ファイルをパッケージ化するには webpack 構成ファイルが必要です: webpack.config.js
const path = require('path'); var nodeExternals = require('webpack-node-externals'); module.exports = { entry: { server: './src/server.ts' }, resolve: { extensions: ['.ts', '.js'], alias: { 'main.server': path.join(__dirname, 'dist', 'server', 'main.bundle.js') } }, target: 'node', externals: [nodeExternals()], output: { path: path.join(__dirname, 'dist'), filename: '[name].js' }, module: { rules: [ { test: /\.ts$/, loader: 'ts-loader' } ] } }
パッケージ化の便宜上、次のようになります。次のように、数行のスクリプトを 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 ノード dist/server.js ファイルを実行します。パッケージ化されています
http://localhost:4200/ を開くと、プロジェクトのメイン ページが通常どおり表示されます
上から HTML ドキュメントがサーバーから直接レンダリングされていることがわかります。 次に、リクエストを試してみます。データ。
注: このプロジェクトの明示的な (クリック可能なメニュー) ルーティング初期化はデータを要求しませんが、単語の説明の詳細ページは ngOnInit() メソッドでデータを取得します。例: http://localhost:4200/通常のサーバーサイドレンダリングプロジェクトの最初の画面の初期化データのリクエストは、サーバー側とクライアントにそれぞれ一度送信され、サーバー側で実行されません。
問題を発見したら、この落とし穴を解消しましょう
サーバーがデータを取得したかどうかを区別するためにマークが使用されている場合を想像してください。データが取得されていない場合、クライアントはそれを要求します。 . データが取得されている場合、リクエストは送信されません
もちろん、Angular には Transfer State 用の Angular Modules が用意されています
では、実際にどのように使用するのでしょうか。以下を参照してください
ピットを埋めるリクエスト
サーバーの入り口とクライアントの入り口にそれぞれ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) } } }
コードはシンプルで、十分明確であり、上で説明したとおりです。原理は同じです
ここで、DOMContentLoaded 時にコードを実行して TransferState が適切に動作するように、main.ts ファイルを少し調整するだけです:
import { enableProdMode } from '@angular/core' import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' import { AppModule } from './app/app.module' import { environment } from './environments/environment' if (environment.production) { enableProdMode() } document.addEventListener('DOMContentLoaded', () => { platformBrowserDynamic().bootstrapModule(AppModule) .catch(err => console.log(err)) })
ここに移動して npm を実行しますbuild && node dist/server.js を実行してから、コンソールで http://localhost:4200/detail/add を更新して、次のようにネットワークを表示します:
XHR カテゴリでリクエストが開始されていないことがわかります。 Service-Worker のキャッシュのみがヒットします。
これまでのところ、プロジェクトはすべて正常に実行されており、他のバグは見つかっていません。
概要
2018 年の最初の記事の目的は、すべての人気フレームワークにおけるサーバーサイド レンダリングの実装を調査し、最後まで試されなかったフレームワークである Angular をオープンすることです。
もちろん、Orange はまだフロントエンドの小学生です。実装方法はあまり明確ではありません。間違いがある場合は、それを理解していただければ幸いです。私を啓発します。
最後のGithubアドレスは前回の記事と同じです: https://github.com/OrangeXC/udao
以上は皆さんのためにまとめたもので、今後皆さんのお役に立てれば幸いです。
関連記事:
以上がAngular5 を使用してサーバー側レンダリングの実践を実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。