Универсальный Angular используется для рендеринга html на стороне сервера и отправляет обратно в браузер. Google не может сканировать выражение привязки, указанное в метатеге. Поэтому требуется универсальный Angular. Он включает два метода.
- renderModuleFactory() использует API @angular/platform-server для предварительного рендеринга вашего приложения.
- @nguniversal/express-engine можно использовать в среде ExpressJS.
Начиная
В этом руководстве вы узнаете, как интегрировать ваше приложение angular-cli с angular universal, и мы используем @nguniversal/module-map-ngfactory-loader для ленивой загрузки наших маршрутов. Установите следующие зависимости.
//install the following dependencies $ npm install --save @angular/platform-server @nguniversal/module-map-ngfactory-loader ts-loader express @nguniversal/express-engine
Шаг 1: Подготовка нашего приложения к универсальному рендерингу
Сначала мы должны добавить appId в AppModule с помощью .withServerTransition(), и appId может быть предоставлена любая строка, которая зависит от вас.
@NgModule({ bootstrap: [AppComponent], imports: [ // Add .withServerTransition() to support Universal rendering. // The application ID can be any identifier which is unique on // the page. BrowserModule.withServerTransition({appId: 'my-app'}), ... ],})
export class AppModule {}
Далее мы собираемся создать AppServerModule, который похож на файл app.module.ts. Файл app.server.module.ts рекомендуется запускать на сервере и копировать и вставлять следующий код, представленный ниже.
Примечание. Модуль ModuleMapLoaderModule необходим для ленивой загрузки наших маршрутов при рендеринге на стороне сервера
import {NgModule} from '@angular/core';
import {ServerModule} from '@angular/platform-server';
import {ModuleMapLoaderModule} from '@nguniversal/module-map-ngfactory-loader';
import {AppModule} from './app.module';import {AppComponent} from './app.component';
@NgModule({
imports: [
// The AppServerModule should import your AppModule followed // by the ServerModule from @angular/platform-server. AppModule,ServerModule, ModuleMapLoaderModule // <-- *Important* to have lazy-loaded routes work ], // Since the bootstrapped component is not inherited from your // imported AppModule, it needs to be repeated here. bootstrap: [AppComponent], }) export class AppServerModule {}
Шаг 2: Создайте файл Main.server.ts и tsconfig.server.json:
Затем мы должны создать файл main.server.ts, который помещается в нашу папку src, и экспортировать AppServerModule. Экспортированный AppServerModule используется для вызова нашего файла сервера и перемещения приложения в производственный режим для более быстрой загрузки.
import {enableProdMode} from '@angular/core';export { AppServerModule } from './app/app.module.server';
enableProdMode();
Настройте ts.config.json:
Теперь просто скопируйте содержимое файла tsconfig.app.json в tsconfig.server.json, и нам нужно внести некоторые изменения в файл tsconfig.server.json. Измените цель «module» на «commonjs», добавьте код angularCompilerOptions и установите entryModule в AppServerModule Указывается символом #.
{ "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/app", "baseUrl": "./", "module": "commonjs", "types": [] }, "exclude": [ "test.ts", "**/*.spec.ts" ], "angularCompilerOptions": { "entryModule": "app/app.server.module#AppServerModule" }
}
Шаг 3: Создайте наше второе приложение для сервера в .angular-cli.json:
Angular-cli содержит массив приложений, поэтому мы должны включить новое приложение в массив и скопировать конфигурацию клиентского приложения в серверное приложение и установить платформу в качестве сервера, как показано ниже. Удалите полифиллы и установите выходной каталог «outDir»: «dist- сервер».
Примечание. Мы создали два приложения, поэтому выходным каталогом первого приложения по умолчанию является dist. Укажите другое имя каталога для сервера
"apps": [ { "root": "src", "outDir": "dist", "assets": [ "assets", "favicon.ico" ], "index": "index.html", "main": "main.ts", "polyfills": "polyfills.ts", "test": "test.ts", "tsconfig": "tsconfig.app.json", "testTsconfig": "tsconfig.spec.json", "prefix": "cloud", "styles": [ "styles.css" ], "scripts": [ ], "environmentSource": "environments/environment.ts", "environments": { "dev": "environments/environment.ts", "prod": "environments/environment.prod.ts" } }, { "name":"universal", "platform": "server", "root": "src", "outDir": "dist-server", "assets": [ "assets", "favicon.ico" ], "index": "index.html", "main": "main.server.ts", "tsconfig": "tsconfig.server.json", "prefix": "app", "styles": [ "styles.css" ], "scripts": [ ], "environmentSource": "environments/environment.ts", "environments": { "dev": "environments/environment.ts", "prod": "environments/environment.prod.ts" } }
]
Создайте файл Seo_service.ts. и служебный файл используется в любом месте нашего приложения, которое используется для обновления метатегов в index.html.
import { Injectable } from '@angular/core'; import { Meta, Title } from '@angular/platform-browser'; export interface SeoData { title?: any; description?: any;}@Injectable()export class SeoService { // This part is happended at the end of head>title private siteTitle = 'Cloud2torial'; constructor(private _title: Title, private _meta: Meta) {} public setHeaders(data: SeoData) { this._title.setTitle([data.title, this.siteTitle].join(' | ')); this._meta.updateTag({ content: data.description, name: 'description' },
);
Импортируйте SeoService в app.module.ts:
import {SeoService} from './seo_service';
@NgModule({providers: [SeoService]})
Обновите метатеги в App.component.ts:
import { Component, OnInit } from '@angular/core'; import {SeoService} from './seo_service'; @Component({ selector: 'cloud-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit{ constructor(private _seo: SeoService) {} ngOnInit() { this._seo.setHeaders({ title: 'New App', discription:'App discription' })
}
Шаг 5: Теперь переведите приложение в рабочий режим:
Создайте приложение для производства, поэтому мы указали два приложения в .angular-cli.json: первое приложение массива 0, а второе приложение будет приложением 1.
ng build --prod && ng build --prod --app 1 --output-hashing=none
Приведенная выше команда создаст папку Dist и Dist-server, и теперь работа на стороне клиента завершена! Далее нам нужно создать экспресс-сервер с angular universal.
Шаг 6: Создайте файл server.js
Теперь нам нужно создать файл server.js с фреймворком expressjs, и я использую @nguniversal/express-engine, вы также можете использовать renderModuleFactory(), который предоставляется платформой-сервером. Требуются пакеты для использования в nodejs, и теперь все маршруты отображаются и отправляются обратно в браузер.
'use strict'; require('zone.js/dist/zone-node'); require('reflect-metadata'); const express = require('express'); const ngUniversal = require('@nguniversal/express-engine'); const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader'); const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist-server/main.bundle');
function angularRouter(req, res) { res.render('index', {req, res}); }
const app = express(); app.engine('html',ngUniversal.ngExpressEngine({ bootstrap: AppServerModuleNgFactory, providers: [provideModuleMap(LAZY_MODULE_MAP)] }));
app.set('view engine', 'html'); app.set('views','dist'); app.use(express.static(`${__dirname}/dist`)); app.get('*', angularRouter); app.listen(3000, () => { console.log('Listening on port 3000'); });
Теперь наш сервер работает на порту 3000. Вы также можете указать каждый маршрут для рендеринга в виде html, но я использую *, чтобы включить все маршруты для рендеринга.
Вывод
node server.js
Выполните следующую команду в каталоге проекта и перейдите в браузер с URL-адресом «http://localhost:3000» и нажмите Ctrl + U в браузере Chrome, чтобы увидеть отображаемый HTML.