Большинство web-приложений и web-фреймворков используют архитектуру, не позволяющую разделить ui и backend разработку. Тем самым нет возможности разделить команду на узкоспециализированных frontend и backend разработчиков. Вне зависимости от предпочтений разработчика ему приходится иметь понимание как о слое представления, так и о слое логики. Если ui-разработчик знает только о том, как запустить сервер, и о модели данных — это огромная удача. В плохих случаях ui-разработчику необходимо провести полную сборку проекта чтобы увидеть изменения строчки в javascript файле, или знать о языке jsp файлов чтобы поменять стиль элемента. Формирование и передача на сервер обработанных html файлов так-же пагубно влияет на производительность сервера и сети.
В наше время современных браузеров с поддержкой HTML5, WebSocket и Full Ajax приложений больше нет необходимости забивать backend-сервера чем-то отличным от бизнес логики. Вся ui-разработка может вестись на nginx сервере с заглушками api сервисов. А фреймворки для авто-генерации документации помогут и ui, и backend разработчикам снизить затраты на коммуникацию. Передача одних лишь json данных также существенно снизит нагрузку на сервера. Ведь сжатый javascript код ui-клиента можно держать в кеше приложения.
Но если современные бразуеры с легкостью справятся с возросшей ответственностью, то поисковым системам нужна помощь.
Для правильной индексации приложения на angularjs нам понадобятся следующие вещи:
- sitemap.xml
- html5Mode
- nginx
- old fashion backend server
HTML5 mode
Html5 mode превращает angularjs routes из вида example.com/#!/home
в вид example.com/home
(все href
ссылки должны также указывать на url без hashbang). Чтобы включить html5mode нужно выполнить:
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('!');
я оставляю !
для совместимости с браузерами, не поддерживающими javascript
Теперь необходимо чтобы наш nginx сервер отправлял запросы с example.com/home
на главный index.html
файл приложения. Для этого укажем в конфиге следующую директиву:
location / {
expires -1;
add_header Pragma "no-cache";
add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
root /var/web;
try_files $uri $uri/ /index.html =404;
}
Строка try_files $uri $uri/ /index.html =404;
означает что теперь все несуществующие url будут переадресованы на index.html
файл, сохранив при этом url в адресной строке браузера.
Это решение уже является рабочим (а также совместимым с старым hashbang форматом ссылок) и если ваше приложение не должно индексироваться поисковыми системами то можно закончить.
SEO
Теперь поможем поисковику обработать наше приложение правильно. Для этого мы подготовим подсказки для бота и сгенерируем snapshot страниц. Для начала расскажем ему о том, какие страницы нужно индексировать с помощью sitemap.xml
файла. Простейший вариант файла состоит из ссылок на страницы и даты их последнего обновления (более подробный формат есть на сайте www.sitemaps.org/):
<urlset xmlns='http://www.sitemaps.org/schemas/sitemap/0.9'>
<url>
<loc>http://senior-java-developer.com/java/basics</loc>
<lastmod>2013-07-12</lastmod>
</url>
<url>
<loc>http://senior-java-developer.com/</loc>
<lastmod>2013-07-12</lastmod>
</url>
</urlset>
Отлично, поисковик будет запрашивать ссылки нашего сайта и получать контент index.html
т.к. никакой обработки javascript в поисковые боты не встроено. Расскажем боту что за технической страницей `index.html` спрятан реальный контент. Для этого в заголовок страницы добавим:
<meta name="fragment" content="!" />
Это даст боту возможность сделать следующий шаг. Увидев fragmet=!
бот запросит страницу еще раз, но добавит в конец url параметр ?_escaped_fragment_=
. Подскажем nginx что запросы с данным параметром нужно отправлять в другое место:
if ($args ~ "_escaped_fragment_=(.*)") {
rewrite ^ /snapshot${uri};
}
location /snapshot {
proxy_pass http://api;
proxy_connect_timeout 60s;
}
Вот и все, теперь все запросы от бота будут отправлены к api backend серверу.
Real url | Bot url | Backend url |
example.com |
example.com/?_escaped_fragment_= |
localhost:8080/snapshot/ |
example.com/home |
example.com/home?_escaped_fragment_= |
localhost:8080/snapshot/home |
Для формирования снапшота я использую thymeleaf. Т.к. и thymeleaf, и angularjs используют html5 атрибуты можно использовать единый файл шаблона, однако я предпочитаю не смешивать их.
Строчка из html view выглядела бы примерно так:
<div ng-bind="text" th:text="${text}"></div>
Автор: fls_welvet