Angularサーバ側レンダリングのピット

6985 ワード

Angularの公式ドキュメントには、サーバ側のレンダリングを説明するためのAngular Universalの章があります.しかし、サーバレンダリングを使用すると、多くのピットに遭遇します.
1.文書内のピット
ドキュメントでは、サーバレンダリングの手順や構成について概説していますが、ドキュメントには多くの問題があります.
1.1 index-aot.html
Expressルーティングを使用する場合、以下のコードがありますが、ドキュメントにはindex-aot.htmlの説明はありません.
server.get(['/', '/dashboard', '/heroes', '/detail/:id'], (req, res) => {
    res.render('index-aot.html', {req});
});
次のindex-aot.htmlを使用できます.ここで重要なのは、3つのJSファイルが追加されていることです.-shim.min.jsは、ES 6のpromises、symbols、collections、iteratorsなど、ブラウザにまだサポートされていない新しい特性を提供します.-zone.jsはAngular依存のコアライブラリです.-build.jsはaotコンパイル後のファイルです

<html>
  <head>
    <base href="/">
    <title>Angular Tour of Heroestitle>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="styles.css">

    
    <script src="node_modules/core-js/client/shim.min.js">script>

    <script src="node_modules/zone.js/dist/zone.js">script>
  head>

  <body>
    <my-app>Loading...my-app>
  body>
  <script src="dist/build.js">script>
html>
1.2 webpack output.path
Webpackの新しいバージョンでoutput.pathは絶対パスである必要があります.ドキュメントで相対パス構成を使用するとエラーが発生します.
- configuration.output.path: The provided value "src/dist" is not an absolute path!
だから
output: {
  path: 'src/dist',
  filename: 'server.js'
}
に改心
var path = require('path');

output: {
  path: path.join(__dirname, 'src/dist'),
  filename: 'server.js'
}
1.3 webpack tsConfigPath
文書ではWebpackの構成は2つの場所を変更する必要がありますが、実は3つの場所です.
The Webpack configuration file for Universal is almost the same as for AOT. There are two differences:
1.The entry points 2.The output file name
AotPluginのtsConfigPathはtsconfig-uni.jsonに変更する必要があります
plugins: [
    new ngtools.AotPlugin({
      tsConfigPath: './tsconfig-uni.json'
    }),
    new webpack.optimize.UglifyJsPlugin({ sourceMap: true })
  ],
1.4 @angular/animations
npmを使用して@angular/platform-serverをインストールすると@angular/animationsが欠落しているとプロンプトが表示され、後のbuildをインストールしない場合もエラーが表示されます.
Could not resolve @angular/animations/browse ...
npmでインストールすればいいです
npm i @angular/animations --save
2.その他の質問
2.1 window is undefined
サーバレンダリングはノード環境で実行されるため、Windowsというグローバルオブジェクトは存在しないため、例えばLocalStorage、Cookieなども使用できません.
だからwindow.localStorageを呼び出すとwindow is undefinedのエラーが投げ出されます.
最も簡単な方法は、WindowsオブジェクトがなければLocalStorageを使用しないと判断することです.
if (typeof window !== 'undefined') {
  const token = localStorage.getItem('token');
}
より良い方法は、サーバ側でstorageオブジェクトをシミュレートし、PLATFORM_を使用することです.IDは現在のプラットフォームがブラウザかサーバかを判断する
@Injectable()
export class LocalStorageService {

  storage: {};

  constructor(@Inject(PLATFORM_ID) private platformId: Object) {
    if (isPlatformBrowser(this.platformId)) {
      this.storage = window.localStorage;
    }
    if (isPlatformServer(this.platformId)) {
      this.storage = {};
    }
  }

  get(key: string) {
    return this.storage[key];
  }

  set(key: string, value: string) {
    this.storage[key] = value;
  }

  remove(key: string) {
    delete this.storage[key];
  }

}
2.2 setTimeout & setInterval
settimeoutを使用すると、サーバ側のレンダリング速度が低下し、setIntervalを使用するとレンダリングに失敗する可能性があります.
AppComponentのコンストラクション関数でsetIntervalが使用され、オフになっていない場合は、ブラウザ側で応答しません.タイマーを実行すると、レンダリングがまだ終わっていないと判断して失敗したと推測されます.