| Пятница, 13 октября, 2017
Метки: Angular, TypeScript, SEO Комментарии: 3
Одностраничный сайт разработан. Открыт в общем доступе. Но как его продвигать? SEO (Search Engine Optimization) продвижение становится очень затруднительным из-за того, что другие сайты, социальные сети и поисковые системы не видят содержимое вашего сайта, так как оно генерируется в браузерах пользователей, а данные для них подгружаются ajax-запросами. Решаем эту проблему и делаем наш одностраничник информативным для других сайтов с помощью Angular Universal.
Библиотеки для разработки одностраничных приложений все больше и больше завоевывают внимание JavaScript-программистов по всему миру. Такие приложения работают непосредственно в браузере, генерируют содержимое страницы прямо на клиенте, выполняют навигацию и переключение страниц затрагивая сервер только для получения данных, и при этом нагружают сервер и сеть по минимуму. Кроме того, такие приложения обеспечивают прекрасную скорость работы, а также быстрые и удобные интерфейсы для взаимодействия с пользователем. Но есть один недостаток, приложение должно быть проиндексировано поисковыми машинами.
Многие поисковые сайты и социальные сети такие как Facebook и Twitter ожидают чистый HTML для вычитывания из него мета тегов и текстов страниц. Они не могут определить, когда Javascript закончит генерацию страницы и поэтому не получают ценной информации, а всего лишь малый набор тегов. Хотя такие монстры как Google хорошо генерируют страницы динамических сайтов, читают их и индексируют, но пользователи все равно впадают с ступор при попытке поделится ссылкой в социальной сети.
И для этого нужен сервер, который будет выдавать готовый HTML-код. Нам нужно, чтобы поисковые машины, социальные сети и просто пользователи приложения видели страницы, сгенерированные уже на сервере, так как это 100% гарантированный способ доставки содержимого любому клиенту. В этом нам поможет Angular Universal.
Официальный сайт (universal.angular.io) заявляет, что Angular Universal — это рендеринг приложений Angular 2+ на серверной стороне. Фактически это промежуточное приложение (middleware) между node.js и Angular, которое связывает все лучшие качества одностраничных приложений (взаимодействие с пользователем, быстродействие) и статичных сайтов, которые прекрасно дружат с поисковой оптимизацией.
Внимание, для повтора всех шагов, описанных в статье, убедитесь, что у вас глобально установлены версии не ниже (тестировалось, именно с этими с версиями): node.js (v6.9.2), npm (3.10.5), Angular CLI 1.4.4. Проверить версии можно командами соответственно: node -v, npm -v, ng -v. Данная утилита Angular CLI 1.4.4 соберет вам проект с Angular 4.2.x. Если Angular CLI установлен ниже, то переустановите его во избежание нестыковок и возможных проблем.
Angular CLl на момент написания статьи распространяется с именем @angular/cli вместо anguar-cli. Переустановка пакета состоит из трех шагов:
Теперь у нас все готово для сборки обычного Angular-проекта. Создаем обычный проект с помощью команды ng new angular-universal-demo. Более подробно с созданием и запуском проекта на Angular 4 можно тут. По завершении создания запускаем этот проект. В браузере открываем просмотр кода страницы и видим в теле документа body пустой тег app-root, в котором Angular генерирует содержимое страницы.
Это и есть главная проблема, которая нас не устраивает, также в основном поисковые системы не видят наших страниц и не могут их правильно прочитать.
BrowserModule.withServerTransition({appId: 'my-app'})AppId можем назначить любое.
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: [ // В AppServerModule должен быть импортирован AppModule // вместе с ServerModule из @angular/platform-server. AppModule, ServerModule, ModuleMapLoaderModule, ], // Так как главный загрузочный компонент не наследуется из // AppModule, то тут тут мы повторяем его подключение. bootstrap: [AppComponent], }) export class AppServerModule {}Здесь мы используем два новых модуля поэтому подключим их:
import { environment } from './environments/environment'; import { enableProdMode } from '@angular/core'; if (environment.production) { enableProdMode(); } export {AppServerModule} from './app/app.server.module';
{ "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/app", "baseUrl": "./", // Изменяем формат модуля на "commonjs": "module": "commonjs", "types": [] }, "exclude": [ "test.ts", "**/*.spec.ts" ], // Добавляем "angularCompilerOptions" и указывает AppServerModule // как входящий модуль "entryModule". "angularCompilerOptions": { "entryModule": "app/app.server.module#AppServerModule" } }
{ "platform": "server", "root": "src", "outDir": "dist/dist-server", "assets": [ "assets", "favicon.ico" ], "index": "index.html", "main": "main.server.ts", "test": "test.ts", "tsconfig": "tsconfig.server.json", "testTsconfig": "tsconfig.spec.json", "prefix": "app", "styles": [ "styles.css" ], "scripts": [], "environmentSource": "environments/environment.ts", "environments": { "dev": "environments/environment.ts", "prod": "environments/environment.prod.ts" } }
require('zone.js/dist/zone-node'); require('reflect-metadata'); const express = require('express'); const { ngExpressEngine } = require('@nguniversal/express-engine'); // Import module map for lazy loading const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader'); // Import the AOT compiled factory for your AppServerModule. // This import will change with the hash of your built server bundle. const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require(`./dist-server/main.bundle`); const app = express(); const port = 8000; const baseUrl = `http://localhost:${port}`; // Set the engine app.engine('html', ngExpressEngine({ bootstrap: AppServerModuleNgFactory, providers: [ provideModuleMap(LAZY_MODULE_MAP) ] })); app.set('view engine', 'html'); app.set('views', './'); app.use('/', express.static('./', {index: false})); app.get('*', (req, res) => { res.render('index', {req, res}); }); app.listen(port, () => { console.log(`Listening at ${baseUrl}`); });Две новые библиотеки нужно установить:
"build:dynamic": "ng build --prod && ng build --prod --app 1 --output-hashing=false && cpy ./server.js ./dist", "serve:dynamic": "npm run build:dynamic && cd dist && node server"Установим npm install cpy-cli --save-dev.
Набираем команду npm run serve:dynamic, ждем выполнения. В логах мы увидим сборку двух приложений, так как в angular-cli.json мы прописали еще одно серверное. Открываем приложение в браузере. Мы увидим тоже самое, что мы видели запуская команду ng serve. Но посмотрим на исходный код страницы в браузере.
Мы увидим совсем другую картину. Сейчас мы видим внутри тега app-root разметку и тексты.
Это тот вид, которого мы добивались и который будут видеть поисковые системы и другие сайты. Серверный рендеринг открывает широкие перспективы для разработки сайтов программистами активно использующих Angular 4.
Пример проекта можно посмотреть тут https://github.com/ang4-examples/angular-universal-demo
Copyright © CodeHint.ru 2013-2024 (v2.4.7 - работает на Angular Universal)Калькулятор инвест-портфеля