Heim > Artikel > Web-Frontend > Verwendung von Angular5 zur Implementierung serverseitiger Rendering-Praktiken
Dieser Artikel stellt hauptsächlich die detaillierte Erläuterung der serverseitigen Rendering-Praxis von Angular5 vor. Jetzt teile ich ihn mit Ihnen und gebe ihn als Referenz.
Dieser Artikel setzt die Entwicklung basierend auf dem vorherigen Angular5-Artikel fort. Der obige Artikel befasst sich mit dem Prozess der Erstellung von Angular5 Youdao Translation und den Lösungen für die aufgetretenen Probleme.
Dann wurde die Benutzeroberfläche von Bootstrap4 auf Angular Material geändert. Ich werde hier nicht auf Details eingehen, was mit der Änderung der Benutzeroberfläche zu tun hat.
Wer die vorherigen Artikel gelesen hat, wird feststellen, dass der Inhalt der Artikel auf serverseitiges Rendering, Vue’s Nuxt und React’s Next ausgerichtet ist.
Vor dieser Überarbeitung habe ich auch versucht, Verpackungsbibliotheken der obersten Ebene wie nuxt.js und next.js zu finden, was viel Zeit sparen kann, aber ohne Erfolg.
Ich habe mich schließlich für Angular Universal (universelle (isomorphe) JavaScript-Unterstützung für Angular) entschieden, eine isomorphe Front-End- und Back-End-Lösung, die seit Angular2 verfügbar ist.
Das werde ich nicht tun Gehen Sie hier detailliert auf den Inhalt des Dokuments ein. Versuchen Sie, Angulars SSR in einer leicht verständlichen Sprache darzustellen
Voraussetzung
Das oben beschriebene udao-Projekt ist Völlig kompatibel mit Angular-Cli, von der Konstruktion bis zur Verpackung, wodurch dieser Artikel auch universell für alle Angular5-Projekte anwendbar ist, die mit Angular-Cli erstellt wurden.
Erstellungsprozess
Zuerst die Serverabhängigkeiten installieren
yarn add @angular/platform-server express yarn add -D ts-loader webpack-node-externals npm-run-all
Hier ist zu beachten, dass die Versionsnummer von @angular/platform-server am besten ist Basierend auf der aktuellen Version. Installieren Sie die Angular-Version, z. B.: @angular/platform-server@5.1.0, um Versionskonflikte mit anderen Abhängigkeiten zu vermeiden.
Datei erstellen: 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 { }
Datei aktualisieren: 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 { }
Wir brauchen einen Master Datei zum Exportieren des Servermoduls
Erstellen Sie die Datei: src/main.server.ts
export { AppServerModule } from './app/app.server.module'
Aktualisieren Sie nun die Konfigurationsdatei von @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" } } ] // ... }
Das //... oben bedeutet, dass es weggelassen wird, aber es gibt keinen Kommentar in JSON, was seltsam aussieht....
Natürlich ist die Konfiguration von .angular-cli.json nicht festgelegt und hängt von den Anforderungen ab. Ändern Sie es selbst.
Wir müssen eine tsconfig-Konfigurationsdatei für den Server erstellen: 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" } }
Dann aktualisieren: src/tsconfig.app. json
{ "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/app", "baseUrl": "./", "module": "es2015", "types": [] }, "exclude": [ "test.ts", "**/*.spec.ts", "server.ts" ] }
Jetzt können Sie den folgenden Befehl ausführen, um zu sehen, ob die Konfiguration gültig ist
ng build -prod --build-optimizer --app 0 ng build --aot --app 1
Das Laufergebnis sollte wie unten gezeigt aussehen
Dann erstellen Sie den Express.js-Dienst und erstellen die Datei: 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}!`) })
Natürlich benötigen Sie eine Webpack-Konfigurationsdatei, um die server.ts-Datei zu packen: 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' } ] } }
Zur Vereinfachung des Paketierens fügen Sie am besten ein paar Skriptzeilen zu package.json hinzu, wie folgt:
"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" }
Versuchen Sie nun, npm run build auszuführen, und Sie werden die folgende Ausgabe sehen:
Knoten Führen Sie die gerade gepackte Knotendist/Server-Datei aus
Öffnen Sie http://localhost:4200/ und die Hauptseite des Projekts wird normal angezeigt
Wie aus den Entwicklertools oben ersichtlich ist, wird das HTML-Dokument vom Server direkt gerendert. Versuchen Sie als Nächstes, Daten anzufordern.
Hinweis: Keine der expliziten (anklickbares Menü) Routeninitialisierungen dieses Projekts fordert Daten an, aber die Detailseite der Worterklärung ruft Daten in der ngOnInit()-Methode ab, zum Beispiel: http:// A seltsam Das Phänomen tritt auf, wenn localhost:4200/detail/add direkt geöffnet wird. Die Anforderung wird einmal an den Server bzw. den Client gesendet. Die Anforderung für die Initialisierungsdaten des ersten Bildschirms des normalen serverseitigen Rendering-Projekts wird ausgeführt serverseitig, aber nicht zweimal auf der Clientseite!
Nachdem wir das Problem entdeckt haben, beseitigen wir die Grube
Stellen Sie sich vor, dass eine Markierung verwendet wird, um zu unterscheiden, ob der Server die Daten empfangen hat. Wenn die Daten nicht abgerufen wurden, fordert der Client sie an. Senden Sie keine Anfrage, wenn die Daten abgerufen wurden.
Natürlich hat Angular sie bereits vorbereitet, das heißt Angular-Module für die Statusübertragung
Wie nutzt man es also eigentlich? Siehe unten
Anfrage zum Ausfüllen der Grube
Führen Sie TransferStateModule am Servereingang bzw. Clienteingang ein
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 { }
Nehmen Sie dieses Projekt im Detail als Beispiel.component.ts und Ändern Sie es wie folgt:
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) } } }
Der Code ist einfach und klar genug und entspricht dem oben beschriebenen Prinzip.
Jetzt müssen wir nur noch kleine Anpassungen an der Datei main.ts vornehmen, um unseren Code auszuführen DOMContentLoaded, damit TransferState normal funktioniert:
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)) })
Gehen Sie hierher, um npm run build && node dist/server.js auszuführen und aktualisieren Sie dann http://localhost:4200/detail/add in der Konsole, um das Netzwerk anzuzeigen wie folgt:
Es wurde festgestellt, dass in der XHR-Kategorie keine Anfrage initiiert wurde, sondern nur der Cache von Service-Worker getroffen wurde.
Alle Fallstricke wurden bisher überwunden. Das Projekt läuft normal und es wurden keine weiteren Fehler gefunden.
Zusammenfassung
Der erste Artikel im Jahr 2018 zielt darauf ab, die Implementierung von serverseitigem Rendering in allen gängigen Frameworks zu untersuchen, und eröffnet Angular, ein Framework, das am Ende nicht ausprobiert wurde.
Natürlich ist Orange immer noch ein Front-End-Grundschüler. Ich weiß nur, wie man es implementiert, und der Quellcode ist nicht sehr klar. Ich hoffe, Sie können mich aufklären.
Die endgültige Github-Adresse ist dieselbe wie im vorherigen Artikel: https://github.com/OrangeXC/udao
Das Obige habe ich für alle zusammengestellt an alle in der Zukunft.
Verwandte Artikel:
Ausführliche Erklärung zur Implementierung von Vuex (ausführliches Tutorial)
Implementierung der WeChat-Zahlung über vue.js
Implementierung der Benutzerberechtigungskontrolle in Vue2.0
Das obige ist der detaillierte Inhalt vonVerwendung von Angular5 zur Implementierung serverseitiger Rendering-Praktiken. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!