Введение
Недавно довелось столкнуться с проектом по доработке когда-то написанной CRM. Цель доработки была в том, чтобы увеличить быстродействие системы при взаимодействии с пользователем и добавить немного нового функционала, а также победить обнаруженные предыдущими разработчиками и так и не побеждённые утечки памяти в JavaScript"е, на котором и был реализован весь пользовательский интерфейс.
Начав заниматься проектом, покопавшись в недрах огромного количества используемых и не очень дружно взаимодействующих между собой библиотек и framework"ов, проведя ряд экспериментов, мы пришли к неожиданному для себя выводу о том, что виной всему… SPA-архитектура.
Пара слов об SPA-архитектуре
Говоря о современном Web"е, всё чаще можно услышать о технологии Single Page Application (SPA), хотя если быть точным, SPA – это собирательное название набора технологий, позволяющих реализовать WEB-приложение, исполняемое WEB-браузером как одна WEB-страница, как, например, реализован сервис Gmail от Google. С точки зрения пользователя, данная технология привлекает в первую очередь быстротой отклика на действия в пользовательском интерфейсе, так как не требуется полной или даже частичной перезагрузки WEB-страницы с сервера, а все визуальные элементы конструируются прямо в браузере с помощью JavaScript путем манипуляций с DOM-структурой документа.
Таким образом, WEB-приложения становятся очень похожи на обычные приложения для рабочих станций, загружающих информацию из сети Интернет, только средой исполнения для них является не операционная система, а браузер, который в результате вынужден нести на себе всю нагрузку, связанную с исполнением стороннего кода, а именно управление памятью, обеспечение безопасного окружения, предоставление функционала для работы с системными функциями и аппаратным окружением и т. п.
Центральное место SPA-архитектуры занимает представление (View) – то, что видит и с чем взаимодействует пользователь. Результатом работы представления является самый обычный HTML, отображаемый браузером. В отличие от «переходных» «WEB 2.0»-приложений, активно работающих с DOM-структурой документа, например, с помощью jQuery или underscore, SPA-приложение использует DOM только для записи изменений, но не для чтения, то есть не для хранения данных. Для хранения данных теперь используется ещё один компонент SPA-архитектуры – модель (Model).
Модель представляет собой совокупность данных, функций для манипуляции с данными и событиями. Все данные модели полностью хранятся в памяти. Для того, чтобы данные, находящиеся в модели, и данные, отображаемые представлением, сохраняли целостность, представление подписывается на события модели, отслеживая таким образом изменения данных в модели. В свою очередь, модель также реагирует на уведомления представления и обеспечивает неразрывную связь WEB-приложения с сервером, выполняя запросы для получения или отправки данных (в частности с применением методологии REST).
Но вернёмся к представлению. Представление - это самая важная и наиболее сложная часть современных SPA. Обычно представления построены вокруг так называемых шаблонов – заготовок, преобразующихся в HTML. Также представление обновляет полученный HTML при изменении модели и наоборот – уведомляет модель о действиях пользователя с представлением, например, о клике мышкой, вводе с клавиатуры или повороте устройства, в результате чего модель может выполнить манипуляции с данными и затем вновь уведомить представление об изменении данных для того, чтобы представление обновило или сгенерировало новый HTML.
Работа классического WEB-приложения (или WEB-сайта) полностью строится поверх кэширования данных: на сервере, на прокси-сервере и на клиенте. Если данные и состояние приложения обновляются очень часто, преимущество от использования кэширования практически нивелируется. Теоретически одностраничное приложение должно меньше эксплуатировать кэш, так как данные загружаются один раз во время жизненного цикла страницы, но на практике это не всегда так, и об этом поговорим ниже.
Особенности CRM, не укладывающиеся в SPA-реализацию
Посмотрим, как мы можем использовать технологию SPA для реализации простой системы управления взаимоотношениями с клиентами, или CRM. В первую очередь нас интересует операционный и аналитический уровни обработки информации.
Возьмём для примера сильно упрощённый набор разделов несложного CRM:
Рабочий стол (Dashboard) - сводка всех данных системы, имеющих смысл для конкретного пользователя.
События - просмотр совместных действий пользователей отделов маркетинга, продаж и производственных отделов.
Клиенты - управление клиентской базой, контактами и компаниями.
Проекты и сделки - управление взаимоотношениями с клиентами.
Задачи - управление рабочим процессом по реализации.
Отчёты - просмотр и управление аналитическими отчётами по накопленной информации.
Профиль - управление профилем пользователя.
В каждом разделе пользователь работает с выборками часто изменяемых данных. Одно из критичных требований к CRM на операционном уровне - это высокое быстродействие при взаимодействии с пользователем при выполнении таких операций, как частое обращение к реестру информации, активное использование сложной фильтрации и сортировка большого объема результатов. Также на аналитическом уровне требуется быстро получать отчёты, статистику, анализ и прогнозирование по различным метрикам и показателям.
В одностраничном приложении вместо страниц мы оперируем «экранами». Каждый экран является частью подсистемы со своим набором шаблонов, модулей, контроллеров, маршрутом и моделью. Когда пользователь осуществляет переход на другой раздел приложения, загружаются данные, шаблоны и модули, требуемые для работы этого раздела, при этом важно, что пользователь находится на той же странице, и текущее состояние во время загрузки нового экрана сохраняется.
Общая модель всей системы является комплексной, состоящей из независимых моделей для каждого экрана. Переключение между экранами вызывает очищение текущего слоя представления и генерации нового слоя, что довольно часто реализовано таким образом, что предыдущий слой просто становится невидимым и вместо него отображается новый, то есть процесс переключения между экранами устроен таким образом, что уже готовые представления после первоначальной загрузки в дальнейшем просто скрываются и отображаются.
Это сильно отличается от классического способа работы с WEB-приложением, где переходы по ссылкам вызывают полную загрузку страниц, что автоматически вызывает освобождение ресурсов и обновление данных, и пользователь может ожидать, что данные всегда актуальны. В условиях быстро меняющихся данных в CRM, чтобы подтвердить ожидания пользователя при SPA-реализации, в процессе перехода между экранами также возникает необходимость заново загружать все данные с сервера, что сводит на нет преимущество хранения всех данных на клиенте, а, следовательно, и применение самой SPA-архитектуры.
Возникающие проблемы при использовании SPA в CRM
Технология SPA вынуждает разработчика писать код крайне осторожно, так как любая ошибка или недосмотр может привести к утечками памяти, и как следствие – к «тормозам» всей системы.
Почему течёт память?
Огромное количество данных, накапливаемых на клиенте в процессе длительной работы, очень быстро становятся неактуальными. Но между тем, эти данные сохраняются в модели, а представления сохраняются в DOM-структуре документа, если не заниматься очисткой целенаправленно. Для того, чтобы держать модель и DOM «в чистоте», необходимо заниматься периодической «уборкой мусора», так как на встроенный сборщик мусора JIT полагаться в данном случае не стоит, ведь ссылки на объекты, как правило, остаются достижимыми, следовательно, ранее созданные и уже не нужные объекты по-прежнему остаются в памяти. Также необходимо учесть, что в JavaScript всегда есть риск потерять все ссылки, но данные так и не будут очищены.
Почему всё тормозит?
К замедлению работы страницы в основном приводят утечки памяти и сложная модель с большим количеством обработчиков событий. При переходе с экрана на экран, при открытии каждой новой формы в процесс браузера загружаются данные, а также исполняемый код (в случае с AMD), который не редко внедряется с помощью конструкций eval(). Модульные framework"и для построения SPA также имеют свою инфраструктуру со своими издержками. Наиболее частая причина – это ошибки и недоработки разработчика, которые допустить очень легко, а отследить крайне сложно. Профилирование и отладка сложных SPA - это дорогое удовольствие: хотя на сегодняшний день инструменты для отладки уже достаточно развитые, сложность отладки с ростом сложности приложения растёт экспоненциально. В результате, проблема решается только модульной отладкой и тестированием, что в свою очередь увеличивает затраты на разработку.
Как эти проблемы находят своё отражение в разработке CRM?
При разработке CRM большое количество экранов и форм, различающаяся логика в зависимости от типа пользователя, его прав и разрешений, очень быстро устаревающие данные, необходимость в периодическом обновлении состоянии всей системы – основные факторы, усложняющие разработку CRM на технологии SPA. Кроме того, в процессе эксплуатации с ростом объёма данных разрастается модель, и, следовательно, увеличивается время на обработку данных, в результате система начинает тормозить даже там, где на тестах вела себя приемлемо.
Проблема «нескольких экранов»
Пользователи браузеров с вкладками, находясь на одной странице, часто открывают отдельные страницы по ссылкам в отдельных вкладках браузера, совмещая их с переходами по ссылкам внутри одной вкладки. Подобную возможность хотелось бы иметь, работая с WEB-приложением: например, чтобы открывать страницу проекта в отдельной вкладке и держать её «на пульсе». В случае с SPA это также возможно, но в таком случае накладные расходы на разработку резко увеличиваются – там, где была экономия в случае загрузки страниц, теперь получается перерасход, так как в каждой вкладке приложение будет загружать весь нужный ему для работы код; теряется смысл SPA как одностраничного приложения, ведь для пользователя Интернет очевидно, что работая в браузере, ссылка должна открывать новую страницу, а не вести себя как обычное приложение.
Таким образом, при разработке CRM-систем SPA-архитектура является, по нашему мнению, менее предпочтительной, чем классическая архитектура многостраничного приложения с активным использованием AJAX для обновления данных.
Изложение проблемы получилось достаточно обширным, поэтому рассказ о ее решении мы оставим для отдельного материала. Всем спасибо, кто прочитал, следите за публикациями.
В этой статье речь пойдет о Single Page Application (SPA). Будут рассмотрены плюсы и минусы web-приложения построенного по принципам одностраничного сайта (SPA)
Что такое SPASingle Page Application - сокращенно SPA, в переводе на русский язык означает “Приложение одной страницы”. Другими словами SPA - это web-приложение, размещенное на одной web-странице, которая для обеспечения работы загружает весь необходимый код вместе с загрузкой самой страницы. Приложение такого типа появились сравнительно недавно, с началом эры HTML5 и SPA является типичным представителем приложений на HTML5.
Как мы знаем, HTML5 это нечто иное как HTML + CSS3 + JavaScript + [несколько новых тегов]. Таким образом, SPA - это приложения написанные на языке JavaScript. И, следовательно, немного перефразировав предыдущие определение получаем:
“SPA - это web-приложение, размещенное на одной странице, которая для обеспечения работы загружает все javascript-файлы (модули, виджиты, контролы и т.д.) , а также файлы CSS вместе с загрузкой самой страницы.”
Если приложение достаточно сложное и содержит богатый функционал, как например, система электронного документооборота, то количество файлов со скриптами может достигать нескольких сотен, а то и тысяч. А “…загрузка всех скриптов…” никоим образом не означает, что при загрузке сайта будут загружены сразу все сотни и тысячи файлов со скриптами. Для решения проблемы загрузки большого количества скриптов в SPA призван API под названием AMD . AMD реализует возможность загрузки скриптов по требованию. То есть, если для “главной станицы” одностраничного портала потребовалось 3 скрипта, они будут загружены стразу перед стартом программы. А если пользователь кликнул на другую страницу одностраничного портала, например, “О программе”, то принцип AMD загрузит модуль (скрипт + разметка) только перед тем как перейти на эту страницу.
Получается немного скомкано: “Одна страница.. другая станица, третья страница… одностраничный портал”. Расставим все точки над “Ё”. Страница сайта, на котором размещены все ссылки на все CSS, и ссылки на скрипты, необходимые для работы SPA мы назовем “Web-страница”. Файл с такой странице обычно называется “index.html” (в ASP.NET MVC может быть index.cshtml или index.vbhtml или даже index.aspx) А страницы, которые переключает пользователь внутри одностраничного портала назовем “модули”.
Давайте рассмотрим плюсы и минуты данного подхода. Зачем всё это нужно и почему SPA так популярен?
SPA: ПлюсыПервым плюсом стоит отметить тот факт, что приложения на SPA отлично работают на устройствах как стационарных, так и мобильных. “Большие” компьютеры, планшеты, смартфоны, и, в конце-концов, простые телефоны (некоторые) могут беспрепятственно работать с сайтами построенных по принципу SPA. Итак, первый “плюс” - работа на большом количестве устройств , а значит, создав одно приложение, вы получаете гораздо большую аудиторию пользователей нежели при использовании стандартного подхода.
Далее второй “плюс” - богатый пользовательский интерфейс , так называемый User Experience. Так как web-страница одна, построить богатый, насыщенный пользовательский интерфейс гораздо проще. Проще хранить информацию о сеансе, управлять состояниями представлений (views) и управлять анимацией (в некоторых случаях).
Третий “плюс” - SPA существенно (в разы) сокращает так называемые “хождения по кругу”, то есть загрузку одного и того же контента снова и снова . Если ваш портал (сайт) использует шаблон, то вместе с основным содержанием какой-либо страницы посетитель сайта обязательно загружает разметку шаблона. Да, кэширование данных на данном этапе развития WWW достигло высочайших результатов, но если нечего кэшировать, то и время, и ресурсы на это не тратятся.
SPA: МинусыЕсли вы программируете на C#, то единственным минусом SPA является необходимость изучения JavaScript. Во всяком случае, других глобальных проблем мне выяснить не удалось.
Составляющие SPAПринципы любого фреймворка (о них поговорим позже), который реализует парадигму SPA должны придерживаться следующих понятий и определений:
- SPA поддерживает клиентскую навигации. Все “хождения” пользователя по модулям-страницам однозначно фиксируются в истории навигации, причем навигация при этом является “глубокой”, то есть если пользователь скопирует и откроет ссылку на внутреннюю модуль-страницу в другом браузере или окне, он попадет на соответствующую страницу.
- SPA размещается на одной web-странице, значит всё необходимое для работы сайта (портала) скрипты и стили должны быть определены в одном месте проекта - на единственной web-странице.
- SPA хранит постоянно состояние (важные переменные) работы клиента (клиентского скрипта) в кэше браузера или в Web Storage.
- SPA загружает все скрипты требующиеся для старта приложения при инициализации web-страницы.
- SPA постепенно подгружает модули по требованию.
Как вы уже наверное догадались, SPA - это абстрактное понятие. Это принцип архитектуры приложения. Давайте поговорим с чего начать при разработке сайта по принципам SPA.
Существует большое количество базовых библиотек (фреймворк - от английского слова framework - “основа, структура, каркас”), которые реализуют принцип Single Page Application. Что дают эти фреймворки:
- обеспечивают базовые принципы для SPA разработки, минимизируя трудозатраты на решение универсальных задач (смотри раздел “Составляющие SPA);
- фреймворки созданы сообществом разработчиков, а значит используют опыт создания сайтов множества программистов;
- фреймворки являются отправной точкой для создания структуры на основе Single Page Application.
Так как я уже много лет работаю на платформе NET, то я буду рассматривать шаблоны Single Page Application на основе ASP.NET. Давайте рассмотрим следующую сравнительную таблицу.
Сравнение возможностей шаблонов SPAВ таблице приведены наиболее распространённые шаблоны для как основа построения Single Page Application приложения. Обратите внимание, синим фоном выделены базовые кирпичики для построения полноценного фреймворка, таких как DurandalJS и HotTowel, которые выделены зеленым цветом.
Итак, следуя данным предоставленных в таблице вы можете создать Single Page Application приложение используя “голый” ASP.NET и KnockoutJS. Однако, реализацию работы с данными (DAL) вам придется писать самостоятельно, впрочем, как и управление навигацией и историей навигации в том числе.
Одностраничное приложение или SPA — single page application — сайт или веб-приложение, в основе которого находится единственный HTML-документ. Обычно в подобном приложении на HTML-странице подключается JavaScript-фреймворки («каркасы» для разработки) тип AngularJS, BackboneJS, Ember.js и др. Эти фреймворки позволяют отображать на странице разное содержимое, в зависимости от действий пользователей и/или состояния URL страницы. Изменение состояния может происходить при нажатии на ссылки, href которых состоит из фрагмента URL начинающегося с символа «#» . Иногда с пары символов «#!», в случае поискового продвижения это сайта (в Яндексе).Содержимое подобного сайта подгружается с сервера при помощи AJAX — асинхронного JavaScript и XML . Для реализации работы через AJAX нужна также реализация части приложения на серверной стороне. Обычно используются скриптовые языки. Для удобства работы и масштабирования системы выбирают REST (архитектура взаимодействия частей приложения).
Одностраничное приложение и JavaScript-фреймворкиДля построения одностраничных приложений используют различные фреймворки:
- backbone.js (рус .)- легкий библиотека, написана автором CoffeeScript и используемая для разработки SPA-страниц с поддержкой REST архитектуры
- ember.js (рус .)- тоже бесплатный JavaScript-фреймворк основанный на модули Модели-Представления-Контроллера (шаблон разработки — MVC)
- angular.js (рус .) — фреймворк, MVC. Один из авторов продолжается заниматься фреймворком, работая в Google.
Можно построить используя работу с селекторами по идентификатору и целевому селектору :target , CSS-свойства для управлением видимостью содержимого (display, visibility и margin). Шаблоны одностраничного сайта включает все необходимое содержимое для работы посетителя. В этом простейшем случае подключать JS-фреймворки обходимости нет.
Псевдокласс:target позволяет выбрать такие HTML-элементы на странице, атрибут id у которых совпадает со значением хеша из адресной строки. Например, если в адресной строке присутствует URI: http://сайт/test-po-html#result, то на HTML-странице будет найден элемент с идентификатором #result и к нему применятся CSS-стили.
Каркас такой страницы может выглядеть так (внимание! Для упрощения используется одинаковая высота у всех страниц. На практике объем содержимого буде разным)
Container{ font: 1em sans-serif; width:600px; min-height:500px; margin:auto; border:1px solid #000; position: relative; } h1,h2,h3,h4,h5,h6 {margin: 0;} .page{ width:600px; height:430px; position: absolute; top:60px; display: none; background-color: #fff; } div:first-of-type{ display:block; z-index: 1; } div:target{ display:block; z-index: 2; }
Одностраничный статичный сайт ссылка1 ссылка2 ссылка3 ссылка4 страница 1 одностраничного сайта страница 2 одностраничного сайта страница 3 одностраничного сайта страница 4 одностраничного сайта
Посмотреть пример работы . Со страницами, использующими JS-фреймворки работа строится другим способом.
В последнее время в работе специалистов Netpeak Agency много сайтов, использующих AJAX-технологии , а также сайтов написанных на JavaScript фреймворках. Без дополнительной оптимизации они не готовы к продвижению в поисковых системах. Поэтому я детально опишу, что такое SPA-сайты, как они работают, и как можно сделать Single Page Application SEO-Friendly.
Статья будет полезна SEO-специалистам и владельцам сайтов, которые хотят перейти на Single Page Application, но не решаются, потому что SPA могут «поссориться» с поисковыми системами.
Что такое SPASPA (Single Page Application ) — одностраничное JavaScript приложение, которое запускается и работает в браузере. В отличии от «традиционного» сайта, архитектура на SPA-сайтах построена так, что рендеринг страницы полностью происходит на стороне клиента, а не на стороне сервера.
В браузере пользователя запускается JavaScript-приложение, а все необходимое содержимое страниц динамически загружается с помощью AJAX. Навигация по сайту происходит без перезагрузки страниц. За счет такой архитектуры, SPA-сайты работают быстрее, чем «традиционные» сайты.
Рассмотрим детальнее, как происходит загрузка и рендеринг содержимого на SPA-сайтах:
Плюсы SPA-сайтов:
- высокая скорость работы;
- быстрая разработка;
- создание версий для разных платформ на основе готового кода (desktop и mobile приложения) .
Минусы SPA-сайтов:
- JavaScript не обрабатывается большинством поисковых систем;
- SPA-сайты не работают без включенного JS в браузере;
- их нельзя анализировать на предмет ошибок популярными программами и инструментами (например, Netpeak Spider).
Популярные JavaScript фреймворки:
- Angular ;
- Meteor ;
- React ;
- Backbone ;
- Ember ;
- Vue ;
- Polymer ;
- Knockout .
На сегодняшний день только поисковый робот Google умеет рендерить содержимое SPA-сайтов, так как использует для рендеринга инструменты на базе Chrome 41. ASK.com использует выдачу Google. Для остальных поисковых систем необходимо наличие контента в коде в формате HTML.
Роботы Google и Yandex могут проиндексировать Single Page Application, если структура сайта соответствует определенным правилам. При этом, для Яндекса необходимо обязательное наличие полной HTML-копии страницы.
Для Google нужно использовать только правильный формат URL. Летом 2018 Google перестанет индексировать HTML-копии страниц , а будет использовать только рендеринг.
(!) Нельзя запрещать индексирование JS и CSS файлов для поисковых роботов Google. Ограничив их индексацию, Google не сможет проиндексировать содержимое SPA-сайтов.
Существует два способа «заставить» поисковых роботов индексировать AJAX-страницы:
Этот способ подразумевает генерацию HTML-копий страниц (снимков) по отдельному URL с использованием параметра «?_escaped_fragment_» .
Использование URL c «#!»Если адреса AJAX страниц формируются с помощью «#» (хеш) , значит нужно заменить их на «#!» (хешбенг) . Например, с https://example.com/#url на https://example.com/#!url .
Google просканирует содержимое по основному URL, а робот Яндекса, обнаружив в URL «#!», запросит снимок страницы. Он заменит «#!» на «?_escaped_fragment_=» и просканирует ее по адресу https://example.com/?_escaped_fragment_=url .
Примеры адресов c «#!» и HTML-копий страниц:
- https://example.com/home#!page → https://example.com/home?_escaped_fragment_=page ;
- https://example.com/index/#!main → https://example.com/index/?_escaped_fragment_=main ;
- https://example.com/#!home/index → https://example.com/?_escaped_fragment_=home/index .
Если на сайте используются «традиционные» URL (https://example.com/url), необходимо указать на всех страницах мета-тег:
Google просканирует содержимое по основному URL, а робот Яндекса, обнаружив на странице мета-тег , запросит HTML-копию страницы. Также добавит в URL параметр «?_escaped_fragment_=» и просканирует ее по адресу https://example.com/url?_escaped_fragment_=
Для популярных фреймворков есть готовые решения, которые заменяют «#!» на «традиционные» URL, например, HTML5 mode для Angular .
(!) В HTML-копии документа мета-тег размещать не следует: в этом случае страница не будет проиндексирована.
(!) На страницах HTML-копий canonical должен либо отсутствовать, либо вести сам на себя.
Например, на странице https://example.com/home?_escaped_fragment_= должен быть указан следующий canonical:
Отдавать HTML-копии страницы по основному URL
Этот способ подходит для SPA-сайта с «традиционными» URL. В чем суть: поисковый робот, запрашивая страницу по основному URL, вместо динамической версии получает HTML-копию страницы.
Определить поискового робота можно по User-Agent. К примеру, по списку роботов Яндекса .
Рендеринг HTML-копийДля того, чтобы поисковые системы смогли проиндексировать содержимое, необходимо реализовать создание HTML-копий страниц.
HTML-копии — это отрендеренные версии страниц SPA-сайта. Например, содержимое исходного кода страницы SPA-сайта выглядит так:
Single Page Application
А вот отрендеренная HTML-копия этой страницы:
TITLE страницы СОДЕРЖИМОЕ СТРАНИЦЫ
Для создания HTML-копий страниц можно:
Существуют много готовых решений для рендеринга HTML-копий, поэтому, если вы используете популярный фреймворк (например, Angular) , на внедрение HTML-копий страниц должно уйти всего несколько часов разработчика.
Рендеринг на своих серверахПодходит для крупных проектов. Для рендеринга HTML-копий на своих серверах можно использовать следующие инструменты:
- PhantomJS ;
- Angular Universal ;
- Angular-SEO .
Подходит для небольших проектов. Для рендеринга HTML-копий на сторонних серверах можно использовать такие инструменты:
- BromBone ;
- Prerender.io ;
- RenderJS.io .
Изоморфный JavaScript позволяет выполнять рендеринг страницы как на сервере, так и в браузере пользователя. Таким образом, при первой загрузке страницы пользователь получает обычную HTML-страницу и JavaScript-приложение. Дальнейшая навигация по сайту для пользователя будет динамической, в то время как поисковые системы каждый раз будут получать HTML-версию страницы.
Такой подход к разработке сайта позволяет убить сразу двух зайцев: сайт получает все преимущества SPA-сайта и возможность продвижения в поисковых системах без дополнительных внедрений.
Рендеринг страницы на SPA-сайтах происходит на стороне клиента, поэтому без дополнительных доработок код ответа несуществующей страницы будет 200 ОК.
SPA-сайты должны корректно обрабатывать несуществующие страницы и отдавать в качестве заголовка страницы 404 ошибку.
Настройка Google AnalyticsСтандартный код Universal Analytics выполняется при загрузке каждой новой страницы, а SPA-сайты подгружают контент динамически, «имитируя» переход между страницами. Для того, чтобы Google Analytics корректно обрабатывал переходы на страницах, необходимо сделать так, чтобы счетчик Universal Analytics активировался каждый раз, когда на сайте меняется URL.
Настроить Google Analytics для SPA-сайтов можно, используя Tag Manager и триггер «History» или — передавая данные вручную.
С помощью Tag Manager и триггера «History» ВыводыSPA-сайты — это настоящее и будущее, поэтому не нужно бояться брать в работу такие проекты. Single Page Application можно «подружить» с поисковыми системами. Чтобы ваш SPA-сайт был дружелюбен как к SEO, так и к пользователям я рекомендую:
Продукты и технологии:
Single-Page Applications (SPA), ASP.NET Web API, Knockout.js, Ember.js, AJAX и HTML5
В статье рассматриваются:
- создание уровня сервисов и веб-клиента AJAX для приложения-примера;
- шаблоны MVC и MVVM;
- связывание с данными;
- создание веб-клиента с применением Knockout.js;
- создание веб-клиента с применением Ember.js.
Одностраничные приложения (Single-Page Applications, SPA) - это веб-приложения, которые загружают одну HTML-страницу и динамически обновляют ее при взаимодействии с пользователем.
SPA используют AJAX и HTML5 для создания гибких и адаптивных веб-приложений без постоянных перезагрузок страницы. Однако это означает, что большая часть работы возлагается на клиентскую сторону, а именно на JavaScript-код. Разработчику для традиционной ASP.NET может быть трудно совершить такой кульбит. К счастью, существует множество JavaScript-инфраструктур с открытым исходным кодом, которые облегчают создание SPA.
В этой статье я пошагово пройду процесс создания простого SPA-приложения. Попутно вы ознакомитесь с некоторыми фундаментальными концепциями создания SPA, в том числе с шаблонами Model-View-Controller (MVC) и Model-View-ViewModel (MVVM), связыванием с данными и маршрутизацией (routing).
О приложении-примереЯ создал приложение-пример для операций с простой базой данных по фильмам (рис. 1 ). В крайнем слева столбце страницы отображается список жанров. Выбор жанра приводит к появлению списка соответствующих фильмов. Кнопка Edit рядом с записью позволяет изменять эту запись. После редактирования можно щелкнуть кнопку Save для передачи обновления на сервер или кнопку Cancel для отмены изменений.
Рис. 1. SPA-приложение для базы данных по фильмам
Я создал две версии этого приложения: одна из них использует библиотеку Knockout.js, а другая - библиотеку Ember.js. Эти две библиотеки основаны на разных подходах, поэтому будет весьма поучительно сравнить их. В обоих случаях клиентское приложение не требовало более 150 строк JavaScript-кода. На серверной стороне я задействовал ASP.NET Web API, чтобы обслуживать JSON для клиента. Исходный код обеих версий вы найдете на github.com/MikeWasson/MoviesSPA .
(Примечание Я создавал приложение, используя RC-версию Visual Studio 2013. В RTM-версии некоторые вещи могли измениться, но они не должны повлиять на код.)
ОбзорВ традиционном веб-приложении при каждом вызове сервера тот осуществляет рендеринг новой HTML-страницы. Это вызывает обновление страницы в браузере. Если вы когда-нибудь писали приложение Web Forms или PHP, этот жизненный цикл страниц должен быть знаком вам.
В SPA после загрузки первой страницы все взаимодействие с сервером происходит через AJAX-вызовы. Эти AJAX-вызовы возвращают данные (не разметку) - обычно в формате JSON. Приложение использует JSON-данные для динамического обновления страницы без ее перезагрузки. Рис. 2 иллюстрирует разницу между этими двумя подходами.
Рис. 2. Сравнение традиционного жизненного цикла страницы с жизненным циклом в SPA
Одно из преимуществ SPA очевидно: приложения более гибкие и адаптивные, свободные от рваного эффекта перезагрузки страницы и ее рендеринга заново. Другое преимущество может оказаться менее очевидным и касается того, как вы проектируете архитектуру веб-приложения. Отправка данных приложения как JSON обеспечивает разделение между презентационной частью (HTML-разметкой) и прикладной логикой (AJAX-запросы плюс JSON-ответы).
Это разделение упрощает проектирование и развитие каждого уровня. В SPA-приложении с тщательно продуманной архитектурой можно изменять HTML-разметку, не касаясь кода, который реализует прикладную логику (по крайней мере, в идеале). Вы увидите это на практике, когда мы будем обсуждать связывание с данными.
В чистом SPA все UI-взаимодействие происходит на клиентской стороне через JavaScript и CSS. После начальной загрузки страницы сервер действует исключительно как уровень сервисов. Клиенту нужно просто знать, какие HTTP-запросы он должен посылать. Ему не важно, как сервер реализует свою часть.
При такой архитектуре клиент и сервис независимы. Вы могли бы полностью заменить серверную часть, которая выполняет сервис, и, пока вы не изменяете API, вы никак не нарушите работу клиента. Верно и обратное: вы можете заменить все клиентское приложение, не изменяя уровень сервисов. Например, вы могли бы написать родной мобильный клиент, который использует этот сервис.
Создание проекта в Visual StudioВ Visual Studio 2013 есть один тип проекта ASP.NET Web Application. Мастер этого проекта позволяет выбрать ASP.NET-компоненты, которые будут включены в проект. Я начал с шаблона Empty, а затем добавил в проект ASP.NET Web API, установив флажок Web API в разделе Add folders and core references for, как показано на рис. 3 .
Рис. 3. Создание нового ASP.NET-проекта в Visual Studio 2013
В новом проекте есть все библиотеки, необходимые для Web API, а также кое-какой конфигурационный код Web API. Я не вводил никаких зависимостей от Web Forms или ASP.NET MVC.
Обратите внимание на рис. 3 , что Visual Studio 2013 включает шаблон Single Page Application. Этот шаблон устанавливает скелет SPA-приложения, основанный на Knockout.js. Он поддерживает вход с применением базы данных с информацией о членстве в группах или с помощью внешнего провайдера аутентификации. Я не стал использовать этот шаблон в своем приложении, потому что хотел показать более простой пример с нуля. Шаблон SPA - отличный ресурс, особенно если вам нужно добавить аутентификацию в приложение.
Создание уровня сервисовЯ использовал ASP.NET Web API, чтобы создать простой REST API для приложения. Не буду здесь вдаваться в детали Web API - подробности вы можете прочитать по ссылке asp.net/web-api.
Сначала я создал класс Movie, представляющий фильм. Этот класс делает две вещи:
- сообщает Entity Framework (EF), как создавать таблицы базы данных для хранения информации о фильмах;
- сообщает Web API, как форматировать полезные данные JSON.
Вы не обязаны использовать одну модель для обеих целей. Например, вам может понадобиться, чтобы схема базы данных отличалась от полезных данных JSON. В этом приложении я ничего не усложняю:
Затем я воспользовался технологией scaffolding в Visual Studio для создания контроллера Web API, который задействует EF в качестве уровня данных. Чтобы применить эту технологию, щелкните правой кнопкой мыши папку Controllers в Solution Explorer и выберите Add / New Scaffolded Item. В мастере Add Scaffold укажите Web API 2 Controller with actions, using Entity Framework, как показано на рис. 4 .
Рис. 4. Добавление контроллера Web API
На рис. 5 приведен мастер Add Controller. Я присвоил контроллеру имя MoviesController. Имя имеет значение, так как URI для REST API основываются на имени контроллера. Я также установил флажок Use async controller actions, чтобы задействовать преимущества новой функциональности async в EF 6. Я выбрал класс Movie в качестве модели и указал New data context, чтобы создать новый контекст данных EF.
Рис. 5. Мастер Add Controller
Мастер добавляет два файла:
- MoviesController.cs - определяет контроллер Web API, который реализует REST API для приложения;
- MovieSPAContext.cs - это в основном склеивающий слой EF, который предоставляет методы для запроса нижележащей базы данных.
В табл. 1 показан REST API по умолчанию, создаваемый технологией scaffolding.
Табл. 1. REST API по умолчанию, созданный технологией scaffolding из Web API
Значения в фигурных скобках являются заменителями для подстановки. Например, чтобы получить фильм с идентификатором, равным 5, URI должен выглядеть так: /api/movies/5.
Я расширил этот API, добавив метод, который находит все фильмы указанного жанра:
Клиент указывает жанр в строке запроса URI. Например, чтобы получить все фильмы жанра Drama, клиент посылает GET-запрос на /api/movies?genre=drama. Web API автоматически связывает параметр запроса с параметром genre в методе GetMoviesByGenre.
Создание веб-клиентаДо сих пор я просто создавал REST API. Если вы отправите GET-запрос на /api/movies?genre=drama, исходный HTTP-ответ будет выглядеть так:
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: application/json; charset=utf-8 Date: Tue, 10 Sep 2013 15:20:59 GMT Content-Length: 240 [{"ID":5,"Title":"Forgotten Doors","Year":2009,"Genre":"Drama","Rating":"R"}, {"ID":6,"Title":"Blue Moon June","Year":1998,"Genre":"Drama","Rating":"PG-13"},{"ID":7,"Title":"The Edge of the Sun","Year":1977,"Genre":"Drama","Rating":"PG-13"}]
Теперь мне нужно написать клиентское приложение, которое делает с этим что-то осмысленное. Базовый рабочий процесс такой:
- UI инициирует AJAX-запрос;
- обновляем HTML для отображения полезных данных ответа;
- обрабатываем AJAX-ошибки.
Вы могли закодировать все это вручную. Например, вот некоторый jQuery-код, который создает список названий фильмов:
В этом коде есть кое-какие проблемы. Он смешивает прикладную логику с презентационной и тесно связан с вашим HTML. Кроме того, писать его весьма утомительно. Вместо того чтобы сосредоточиться на приложении, вы тратите время на написание обработчиков событий и кода, манипулирующего DOM.
Решение заключается в том, чтобы использовать JavaScript-инфраструктуру. К счастью, их выбор довольно велик, и эти инфраструктуры имеют открытый исходный код. К некоторым из более популярных инфраструктур относятся Backbone, Angular, Ember, Knockout, Dojo и JavaScriptMVC. Большинство использует вариации шаблонов MVC или MVVM, поэтому будет полезно вкратце рассмотреть эти шаблоны.
Шаблоны MVC и MVVMКорни шаблона MVC уходят в 80-е годы прошлого века и связаны с ранними графическими UI. Цель MVC - разбиение кода на три уровня со своими обязанностями (рис. 6 ). Вот что они делают:
- модель представляет данные и бизнес-логику предметной области;
- представление отображает модель;
- контроллер принимает пользовательский ввод и обновляет модель.
Рис. 6. Шаблон MVC
Более современная вариация MVC - шаблон MVVM (рис. 7 ). В шаблоне MVVM:
- модель по-прежнему представляет данные предметной области;
- модель представления - это абстрактное отражение представления;
- представление отображает модель представления и посылает пользовательский ввод модели представления.
Рис. 7. Шаблон MVVM
View Model | View Model |
В JavaScript-инфраструктуре MVVM представлением является разметка, а моделью представления - код.
MVC имеет много вариаций, а литература по MVC зачастую противоречива и запутана. По-видимому, это и не удивительно для проектировочного шаблона, который начал свою жизнь со Smalltalk-76 и все еще применяется в современных веб-приложениях. Поэтому, хоть и хорошо знать теорию, главное - понимать конкретную инфраструктуру MVC, используемую вами.
Создание веб-клиента с применением Knockout.jsДля первой версии своего приложения я использовал библиотеку Knockout.js. Knockout следует шаблону MVVM, соединяя представление и модель представления через связывание с данными.
Чтобы создать привязки данных, вы добавляете к HTML-элементам специальный атрибут data-binding. Например, следующая разметка связывает элемент span со свойством genre в модели представления. Всякий раз, когда изменяется значение genre, Knockout автоматически обновляет HTML:
Привязки также могут работать в другом направлении, скажем, если пользователь вводит текст в поле, Knockout обновляет соответствующее свойство в модели представления.
Удобно, что связывание с данными осуществляется декларативно. Вам не требуется подключать модель представления к элементам HTML-страницы. Просто добавьте атрибут data-binding, и Knockout сделает остальное.
Я начал с создания HTML-страницы с базовой разметкой без связывания с данными, как показано на рис. 8 .
Рис. 8. Начальная HTML-разметка
No records found.
(Примечание Я использовал библиотеку Bootstrap для оформления внешнего вида приложения, поэтому в настоящем приложении уйма дополнительных элементов и CSS-классов, управляющих форматированием. Для ясности я убрал все это из кода.)
Создание модели представленияНаблюдаемые объекты (observables) занимают центральное место в системе связывания с данными в Knockout. Наблюдаемым является объект, который хранит какое-то значение и может уведомлять подписчиков об изменении этого значения. Следующий код преобразует JSON-представление фильма в эквивалентный объект с наблюдаемыми полями:
На рис. 9 показана начальная реализация модели представления. Эта версия поддерживает только получение списка фильмов. Средства редактирования я добавлю позже. Модель представления содержит наблюдаемые свойства для списка фильмов, строку ошибки и текущий жанр.
Рис. 9. Модель представления
Заметьте, что фильмы находятся в observableArray. Как и подразумевает его имя, observableArray действует как массив, уведомляющий подписчиков об изменении своего содержимого.
Функция getByGenre выдает AJAX-запрос серверу на получение списка фильмов, а затем заполняет результатами массив self.movies.
При использовании REST API одна из самых хитрых частей - обработка асинхронной природы HTTP. jQuery-функция ajax возвращает объект, реализующий Promises API. Вы можете задействовать метод then объекта Promise, чтобы установить обратный вызов, инициируемый, когда AJAX-вызов завершается успешно, и еще один обратный вызов, запускаемый при неудачном AJAX-вызове:
Теперь, когда у меня есть модель представления, я могу связать ее с HTML через привязки данных. Для полного списка жанров, который появляется на левой стороне экрана, я использую следующие привязки данных:
Атрибут data-bind содержит одно или более объявлений привязок, где каждая привязка имеет форму "привязка: выражение". В этом примере привязка foreach сообщает Knockout перебирать в цикле содержимое массива genres в модели представления. Для каждого элемента в массиве Knockout создает новый элемент
На данный момент щелчок названия жанра ни к чему не приводит, поэтому я добавляю привязку click для обработки событий щелчка:
Это связывает событие щелчка с функцией getByGenre в модели представления. Здесь нужно было использовать $parent, так как эта привязка осуществляется в контексте foreach. По умолчанию привязки в foreach ссылаются на текущий элемент в цикле.
Рис. 10. Добавление привязок в таблицу для отображения списка фильмов
На рис. 10 привязка foreach перебирает в цикле массив объектов movie. Внутри foreach привязки text ссылаются на свойства текущего объекта.
Привязка visible в элементе
Edit |