ホームページ  >  記事  >  ウェブフロントエンド  >  Angular5 を使用してサーバー側レンダリングの実践を実装する

Angular5 を使用してサーバー側レンダリングの実践を実装する

亚连
亚连オリジナル
2018-06-13 17:24:481798ブラウズ

この記事では主に 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

以上は皆さんのためにまとめたもので、今後皆さんのお役に立てれば幸いです。

関連記事:

vuexの実装方法を詳しく解説(詳細チュートリアル)

vue.jsによるWeChat決済の実装

Vue2.0でのユーザー権限制御

以上がAngular5 を使用してサーバー側レンダリングの実践を実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。