webentwicklung-frage-antwort-db.com.de

Serverseitiges Rendern mit Angular4 (Angular Universal)

Ich arbeite an einem Angular4-Webpack-Projekt, das ich hinzufügen wollte AngularUniversal , um serverseitiges Rendern zu ermöglichen. Die meisten Tutorials verwenden jedoch AngularCli. Ich möchte Universal mit Webpack integrieren. Ich habe Folgendes versucht Tutorial Ohne Glück. Kann jemand bitte helfen.

15
RemyaJ

Diese Angular Universal ist nur für Angular 2. Wenn Sie von vorne anfangen wollen, können Sie diese Angular 4 Universal Seed verwenden Alle Funktionen wie:

  • Winkel 4
  • WebPack
  • dev/Prod-Modi
  • SCSS-Kompilierung
  • i18n, SEO und TSLint/codelyzer
  • lazy Loading, Config, Cache

Wenn Sie bereits ein Angular 4-Projekt ausgeführt haben, können Sie Universal integrieren, indem Sie folgende Einstellungen in Ihrem Code vornehmen: 

Installiere diese Pakete: 
npm install @angular/{common,compiler,compiler-cli,core,forms,http,platform-browser,platform-browser-dynamic,platform-server,router,animations}@latest [email protected] --save 

npm install express @types/express --save-dev 

Fügen Sie dies in Ihre Datei app.module.ts ein

import { BrowserModule } from '@angular/platform-browser';
BrowserModule.withServerTransition({
  appId: 'my-app-id'   // withServerTransition is available only in Angular 4
}),

Erstellen Sie folgende Dateien 

src/uni/app.server.ts

import { NgModule } from '@angular/core';
import { APP_BASE_HREF } from '@angular/common';
import { ServerModule } from '@angular/platform-server';
import { AppComponent } from '../app/app';
import { AppModule } from '../app/app.module';
import 'reflect-metadata';
import 'zone.js';
@NgModule({
  imports: [
    ServerModule,
    AppModule
  ],
  bootstrap: [
    AppComponent
  ],
  providers: [
    {provide: APP_BASE_HREF, useValue: '/'}
  ]
})
export class AppServerModule {
}


src/uni/server-uni.ts

import 'zone.js/dist/zone-node';
import 'zone.js';
import 'reflect-metadata';
import { enableProdMode } from '@angular/core';
import { AppServerModuleNgFactory } from  '../../aot/src/uni/app.server.ngfactory';
import * as express from 'express';
import { ngUniversalEngine } from './universal-engine';
enableProdMode();
const server = express();
// set our angular engine as the handler for html files, so it will be used to render them.
server.engine('html', ngUniversalEngine({
    bootstrap: [AppServerModuleNgFactory]
}));
// set default view directory
server.set('views', 'src');
// handle requests for routes in the app.  ngExpressEngine does the rendering.
server.get(['/', '/dashboard', '/heroes', '/detail/:id'], (req:any, res:any) => {
    res.render('index.html', {req});
});
// handle requests for static files
server.get(['/*.js', '/*.css'], (req:any, res:any, next:any) => {
    let fileName: string = req.originalUrl;
    console.log(fileName);
    let root = fileName.startsWith('/node_modules/') ? '.' : 'src';
    res.sendFile(fileName, { root: root }, function (err:any) {
        if (err) {
            next(err);
        }
    });
});
// start the server
server.listen(3200, () => {
    console.log('listening on port 3200...');
});

src/uni/universal-engine.ts

import * as fs from 'fs';
import { renderModuleFactory } from '@angular/platform-server';
const templateCache = {}; // cache for page templates
const outputCache = {};   // cache for rendered pages
export function ngUniversalEngine(setupOptions: any) {
  return function (filePath: string, options: { req: Request }, callback: (err: Error, html: string) => void) {
    let url: string = options.req.url;
    let html: string = outputCache[url];
    if (html) {
      // return already-built page for this url
      console.log('from cache: ' + url);
      callback(null, html);
      return;
    }
    console.log('building: ' + url);
    if (!templateCache[filePath]) {
      let file = fs.readFileSync(filePath);
      templateCache[filePath] = file.toString();
    }
    // render the page via angular platform-server
    let appModuleFactory = setupOptions.bootstrap[0];
    renderModuleFactory(appModuleFactory, {
      document: templateCache[filePath],
      url: url
    }).then(str => {
      outputCache[url] = str;
      callback(null, str);
    });
  };
}

Fügen Sie die Konfiguration in Ihrer Datei tsconfig.ts hinzu, von der ich annehme, dass sie sich im Stammverzeichnis befindet

{
    "compilerOptions": {
        "baseUrl": "",
        "declaration": false,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "lib": ["es2016", "dom"],
        "moduleResolution": "node",
        "outDir": "./dist/out-tsc",
        "sourceMap": true,
        "target": "es5",
        "module": "commonjs",
        "types": ["node"],
        "typeRoots": [
            "node_modules/@types"
        ]
    },
    "files": [
        "src/uni/app.server.ts",
        "src/uni/server-uni.ts"
    ],
    "angularCompilerOptions": {
        "genDir": "aot",
        "entryModule": "./src/app/app.module#AppModule",
        "skipMetadataEmit": true
    },
    "exclude": [
        "test.ts",
        "**/*.spec.ts"
    ]
}

Atten Sie Ihre webpack.config.uni.js im Hauptverzeichnis

const ngtools = require('@ngtools/webpack');
const webpack = require('webpack');
const path = require('path');
const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin");
module.exports = {
    devtool: 'source-map',
    entry: {
        main: ['./src/uni/app.server.ts', './src/uni/server-uni.ts']
    },
    resolve: {
        extensions: ['.ts', '.js']
    },
    target: 'node',
    output: {
        path: path.join(__dirname, "dist"),
        filename: 'server.js'
    },
    plugins: [
        new ngtools.AotPlugin({
            tsConfigPath: './tsconfig.json'
        })
    ],
    module: {
        rules: [
            {
                test: /\.(scss|html|png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
                use: 'raw-loader'
            },
            { test: /\.ts$/,  loader: require.resolve('@ngtools/webpack') },
            {
                test: /\.(png|jpg|woff|woff2|eot|ttf|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                loader: 'url?limit=512&&name=[path][name].[ext]?[hash]'
            },
            { test: /\.scss$/, use: [{
                loader: "style-loader" // creates style nodes from JS strings
            }, {
                loader: "css-loader" // translates CSS into CommonJS
            }, {
                loader: "sass-loader" // compiles Sass to CSS
            }] }
        ]
    }
}

Fügen Sie unten in Ihrer Datei package.json die folgenden Skripte hinzu:

"ngc-build": "ngc -p ./tsconfig.json", // To generate ngFactory file
"build:uni": "webpack --config webpack.config.uni.js",
"serve:uni": "node dist/server.js",

Es gibt bestimmte Dinge, die wir beachten sollten:

  • window, document, navigator und andere Browsertypen - sind auf dem Server nicht vorhanden. Die Verwendung dieser oder einer Bibliothek, die sie verwendet (z. B. jQuery), wird nicht funktionieren. In diesem link finden Sie einige Optionen, wenn Sie wirklich etwas von dieser Funktionalität benötigen.
13
mohit

Winkeluniversal wird nur für Winkel 2.x verwendet. Angular 4.x müssen Sie den Plattformserver verwenden. Beispiele sind wie folgt:

Für Winkel 2.X.X:

AngularClass's Seed-Projekt mit Express/Universal

https://github.com/angular/universal-starter

Für Winkel 4.X.X Verwenden Sie den Winkelplattformserver

https://github.com/ng-seed/universal

Es gibt auch einige andere Beispiele:

5
Deepak Kumar

Das in diesem Tutorial erwähnte Beispiel verwendet das im Abschnitt Angular Resources erwähnte Beispiel. Sie haben kürzlich ihre Dokumente aktualisiert und die ausführliche Dokumentation für die Implementierung von @ angle/universal noch nicht bereitgestellt. Diese war früher die Seite, nach der Sie suchen, aber sie hatte einige Probleme hier . Vielleicht haben sie es deshalb entfernt und beschlossen, es neu zu schreiben.

3
Devesh Jadon

Sie finden ein Angular 4 Tutorial zum serverseitigen Rendern mit Webpack unter this blog .

Eigenschaften:

  • es basiert auf Angular 4
  • es hängt nicht von Angular CLI ab
  • es baut auf Webpack auf
  • der Blog bietet eine schrittweise Anleitung in drei Phasen:
    • phase 1: Ausführen einer serverseitig gerenderten Hello World-Seite in einem Docker-Container (Ich stelle ein vorinstalliertes Docker-Image zur Verfügung, aber die Anweisungen sollten in Ihrer eigenen Umgebung funktionieren. Angular)
    • phase 2: Erstellen Sie einen neuen funktionalen Link auf der Hauptseite
    • phase 3 (optional): Fügen Sie dynamisch ein WordPress Blog POST über RESTful API ein

Das Endergebnis kann auf einem Docker-Host wie folgt angezeigt werden:

(dockerhost)$ docker run -it -p 8002:8000 oveits/angular_hello_world:centos bash
(container)# git clone https://github.com/oveits/ng-universal-demo
(container)# cd ng-universal-demo
(container)# npm i
(container)# npm run start

Ich habe oben Port 8002 ausgewählt, da ich bereits andere Beispiele für die Ports 8000 und 8001 verwende. Wenn der Docker-Host auf Virtualbox ausgeführt wird, benötigen Sie möglicherweise eine Portzuordnung von 8002 des Virtualbox-Hosts zu 8002 der Virtualbox-VM.

Navigieren Sie in einem Browser zu http: // localhost: 8002/blog . Sie sehen den Inhalt eines Blogposts, der von der Wordpress API. Mit Rechtsklick-> Quelltext anzeigen Der HTML-Inhalt wird angezeigt. Dies zeigt, dass es sich um eine serverseitig gerenderte Seite handelt.

PS: Wie das Tutorial, das Sie ausprobiert haben, basiert das Tutorial auf einem Git-Projekt, das ursprünglich von Rob Wormald erstellt wurde , aber mit diesem Fork von FrozenPandaz habe ich Es wurde eine Version gefunden, die auf Angular 4 aktualisiert wurde und besser mit Webpack funktioniert hat (weitere Informationen finden Sie im Anhang von das Blog ).

1
Olli