Когда (не) использовать Angular: создание временной шкалы для Интернета

В январе 2015 года Хронология открылась для масс. В тот же день оно было представлено Apple, а несколько недель спустя оно снова было признано Лучшим приложением января.

Timeline объединяет технологии и журналистику, чтобы вести пользователей через изысканные путешествия, обеспечивая красочный контекст для наиболее актуальных проблем дня.

Во время разработки мы использовали самые продвинутые инструменты, которые могли найти для этой цели: мы уже писали о приложении для iOS и бэкэнде, полностью разработанном на Swift and Go. Здесь мы сосредоточимся на дополнительном веб-приложении Timeline - не менее важном инструменте для выполнения нашей миссии.

Почему мы выбрали угловой

У Timeline с самого начала были амбициозные цели в области пользовательского опыта (UX), поэтому инвестировать в мобильный опыт мирового класса означало перейти на iOS. Но поскольку мы хотели, чтобы пользователи на любой платформе имели доступ к контенту Timeline, нам было естественно выбрать сопутствующее веб-приложение. Мы выбрали AngularJS в качестве фреймворка для этого проекта, поскольку он позволял быстро развиваться и обещал реплицировать iOS UX на веб-платформу.

У нас есть опыт создания нескольких продуктов с использованием AngularJS, в том числе Routific - мощного SaaS-решения для управления автопарком и оптимизации маршрутов - и наша команда разработчиков сразу взялась за дело.

Мощная привязка данных Angular позволяет очень быстро создавать веб-приложения из данных, предоставляемых серверным API. Поскольку состояние видимой страницы автоматически отражает структуры данных в JavaScript, сценарий страницы может управлять содержимым без явного шага применения. Angular позволил нам за очень короткое время предоставить удобную первую версию сайта Timeline.

По мере того, как мы собирали больше данных от пользователей (а также от команды редакторов спецназа Timeline), наш первоначальный дизайн - единая гибкая страница с расширяемыми историями - уступил место более разделенному на части дизайну с главной страницей и отдельными страницами историй. Вскоре единственным остатком нашего первоначального дизайна в стиле iOS стали всплывающие меню, используемые для вторичной навигации.

Из-за этих и других изменений в природе веб-продукта Timeline стало ясно, что нам придется переосмыслить наш выбор Angular в качестве фреймворка.

Почему мы перешли

Хотя Angular является мощным инструментом для создания динамических полнофункциональных веб-приложений, мы обнаружили его ограничения, поскольку веб-сайт Timeline стал больше похож на статический (но очень настраиваемый) веб-сайт. Его привязка данных, столь полезная для динамического обновления контента и ввода данных пользователем, дает мало преимуществ сайту, который состоит в основном из статических страниц.

Angular лучше всего подходит для создания мощных одностраничных приложений (SPA), удерживая пользователя в рамках одного потока, подобного приложению.

Отсутствие преимуществ от основной силы Angular само по себе не заставит нас перейти на другую структуру, но архитектура Angular в качестве платформы влечет за собой ряд технических затрат, которые больше не стоит платить. Эти затраты включают:

  • Плохая поисковая оптимизация (SEO) и социальная интеграция
  • Столкновения с типичным ожидаемым "браузером UX"
  • Требования к тяжелым испытаниям
  • Неутешительная производительность

Каждый из этих недостатков более подробно обсуждается ниже.

Плохое SEO и социальная интеграция

Самым большим недостатком Angular была плохая доступность для парсеров страниц и поисковых роботов. Поскольку каждая страница была полностью собрана с использованием клиентского JavaScript, любой клиент, который не выполнил наш JS-код, увидит только базовый шаблон страницы. Мы смогли обеспечить визуализированное представление сайта для роботов, использующих PhantomJS, но это была сложная, подверженная ошибкам система, в значительной степени отличавшаяся от нормальной работы сайта, и поэтому всегда была «второсортной».

Обслуживание радикально разного контента для разных клиентов - операция по своей сути дорогостоящая, и это увеличивало и без того тяжелую нагрузку на тестирование.

Многие первоначальные проблемы были связаны со сложностью предоставления роботам правильного контента. К ним относятся неточные или заполненные краткие описания историй, когда они публиковались в Facebook и Twitter (и в более мелких сервисах, таких как Slack и Product Hunt); сложно диагностировать проблемы с PhantomJS; и плохое SEO для сайта в целом.

Конфликты пользовательского интерфейса браузера

На протяжении всего периода бета-тестирования нам было сложно интегрироваться с поведением UX браузера по умолчанию, особенно в случае истории и прокрутки. Хотя мы могли дать каждой истории собственный URL-адрес и запись в истории браузера, что позволило использовать кнопки «Назад» и «Вперед» для навигации по сайту, эти отдельные URL-адреса не относились к отдельным страницам. Наш «взлом», вызванный использованием Angular, разрушил ожидания браузера в отношении изменений URL и вызвал взаимодействия, из-за которых UX нашего сайта казался неотшлифованным.

Самая большая проблема заключалась в том, что Timeline как одностраничное приложение конфликтует с хранением браузером позиции прокрутки в своей истории (что с тех пор было решено в Chromium). Для большинства браузеров и большинства веб-сайтов переход к другому URL-адресу с помощью кнопок Назад и Вперед немедленно восстанавливает предыдущую позицию прокрутки. В Angular перемещение вперед и назад запускало множественные переходы прокрутки из-за комбинации действий браузера и изменений в содержимом страницы.

Timeline как одностраничное приложение также привело к отсутствию интеграции с собственными функциями обратной связи браузера - мелочами, которые остаются незамеченными, когда присутствуют, но приводят к чувству неловкости, когда отсутствуют. Например, на обычных веб-сайтах щелчок по ссылке загружает новую страницу с сервера: браузер знает об этом и будет показывать индикатор выполнения или счетчик непосредственно в самом браузере, чтобы указать прогресс. С другой стороны, одностраничные приложения динамически загружают новый контент в фоновом режиме; браузер не предоставляет обратной связи по умолчанию, пока это содержимое не загрузится. При медленном подключении это означало, что обратная связь будет сильно задерживаться, из-за чего будет казаться, что щелчок не зарегистрирован.

Конечно, мы могли бы реализовать собственный механизм обратной связи, например счетчик, но это будет дублировать функции, предоставляемые браузером, и дополнительно гарантировать, что наше приложение не будет выглядеть как «обычный» веб-сайт.

Требования к тяжелому тестированию

Поскольку сайт Angular выполняет практически всю свою обработку на клиенте, логика генерации страницы подвержена различиям между различными реализациями браузера. Хотя сам Angular разработан для обеспечения согласованной платформы для всех браузеров, тонкости в упорядочении событий и рендеринге страниц увеличиваются за счет создания всей страницы на клиенте наряду с функциями взаимодействия, требующими проверки каждого аспекта создания страницы во всех целевых браузерах.

Таким образом, для обеспечения единообразия работы пользователей на всех платформах требуется серьезное тестирование на различных устройствах. Переместив логику генерации страницы на сервер, мы смогли уменьшить объем кода, который необходимо протестировать, что позволит нам тратить больше усилий на разработку.

Низкая производительность и откат

Создание страниц на стороне клиента сильно ограничивает производительность нашего сайта, как фактическую, так и очевидную. Поскольку шаблоны загружаются с использованием клиентского JavaScript, контент не отображается, пока не будет загружен весь сайт и не будет выполнено несколько запросов Ajax. Если посетитель использует браузер, не поддерживаемый Angular, например Internet Explorer 8 или более раннюю версию, сайт не загрузится полностью, останется только пустая страница. Точно так же рендеринг страницы полностью завершился неудачей для пользователей с отключенным Javascript. Это сделало содержание сайта полностью недоступным для этих пользователей. Предоставление резервного контента в этой ситуации потребует создания полного факсимиле сайта на стороне сервера, что требует огромного дублирования усилий.

Что мы сделали вместо этого?

Чтобы лучше интегрироваться с Интернетом в целом, мы решили заменить одностраничное приложение Angular обычным многостраничным веб-приложением. Каждой истории была предоставлена ​​собственная отдельная страница, созданная на сервере с взаимодействиями, обеспечиваемыми клиентским JavaScript.

Мы уже использовали Node.js и Express.js для обслуживания сайта Angular, поэтому было естественным разработать сайт-замену с использованием этих инструментов. Шаблоны Jade уже использовались в качестве основы для исходного HTML, поэтому процесс преобразования для использования Jade для заполнения шаблонов на стороне сервера был относительно простым.

Используя данные-заполнители, мы смогли получить визуальный макет исходного сайта в первые два дня работы по переписыванию.

Основная часть работы по переписыванию была сосредоточена в двух областях: нам нужно было адаптировать код, который взаимодействовал с серверным API, для работы в серверной среде Node, и нам нужно было заново реализовать функции взаимодействия на стороне клиента с использованием jQuery вместо Angular. Компонент взаимодействия с сервером смог сохранить большую часть исходного кода благодаря использованию библиотеки обещаний Bluebird, которую можно использовать для адаптации библиотеки запросов Node к интерфейсу, аналогичному тому, который используется службой Angular $ http.

Код на стороне клиента нужно было переписать с нуля, чтобы отразить другую модель данных для jQuery. Хотя модель jQuery требует от программиста более непосредственного изменения DOM страницы, чем это делает Angular, код значительно упростился благодаря тому, что он полностью отделен от логики генерации страницы.

Редизайн кода сайта с учетом наших текущих целей и планов позволил кодовой базе стать более модульной и удобной в обслуживании. Некоторые функции, которые потребовали длительной переработки существующего кода, стали легко реализуемыми.

Используя новую структуру сайта, мы смогли поддерживать больше устаревших браузеров, потому что Javascript был более простым и совместимым, а также потому, что контент страницы теперь был доступен без какого-либо Javascript вообще (хотя, конечно, ему не хватало бы его богатого взаимодействия Особенности). Теперь сайт быстрее отображается для пользователя благодаря встроенной интеграции с пользовательским интерфейсом обратной связи браузера. Это особенно заметно при медленном соединении, которое всегда возможно на мобильном телефоне.

В результате нашего переписывания с отходом от Angular веб-приложение Timeline стало лучшей заменой нативному приложению на мобильных платформах, отличных от iOS, где это единственный вариант для доступа к контенту Timeline.

Мы по-прежнему остаемся большими поклонниками Angular, учитывая правильный вариант использования. Фактически, после завершения перезаписи уменьшение нагрузки на обслуживание позволило нашей команде тратить больше времени на усовершенствование инструментов разработки временной шкалы. Инструменты разработки - это сложный набор продуктов, богатые требования к взаимодействию которых делают их исключительно подходящими для Angular.

Что нам не хватает в Angular

Хотя переход с Angular на Express, Jade и jQuery был положительным моментом, есть несколько качеств Angular, которые мы все еще упускаем.

Прямой доступ к объектам истории на клиенте

Обе версии сайта считывают объекты структурированной истории из API в формате JSON и генерируют видимое HTML-представление, используя полученные данные. Поскольку создание страницы происходит на стороне клиента с помощью Angular, полная структура данных также доступна для взаимодействия и сценариев аналитики. Это упростило написание этих сценариев, так как вся необходимая информация была легко доступна в клиентском веб-браузере.

Поскольку Jade генерировал HTML, структуры данных истории не были полностью переданы клиенту. Таким образом, любые данные, которые потребуются клиентским скриптам, которые не были напрямую включены в HTML, нужно было добавлять вручную через атрибуты данных HTML5. Хотя jQuery предоставляет удобный интерфейс для данных, хранящихся таким образом, это не так удобно, как автоматическая доступность полной структуры данных.

Постоянство государства

Поскольку приложение Angular было одностраничным, поддерживать согласованное состояние при перемещении по различным частям сайта было несложно. Это было наиболее полезно для нас во время процесса аутентификации пользователя, который, в зависимости от метода аутентификации и статуса регистрации пользователя, мог проходить через последовательность страниц и запросов.

Для аутентификации по электронной почте и Facebook мы смогли перестроить страницу аутентификации в единый поток с последовательностью форм, загружаемых через Ajax. Это позволяло нам сохранять значимое состояние на стороне клиента при перемещении по нескольким страницам, и имело то преимущество, что это была область сайта, где использование функций истории браузера не имело смысла. Для аутентификации Twitter OAuth требуется перенаправление на сам Twitter, а затем обратно на страницу обратного вызова, что усложняет задачу, но в конечном итоге было разработано решение с использованием зашифрованных файлов cookie для хранения конфиденциальной информации о клиенте посредством этого процесса.

Выводы

Хотя Angular - мощная платформа для разработки динамических веб-приложений, она подходит не для всех веб-сайтов. Таким образом, важно подумать, подходит ли оно для конкретного приложения, прежде чем создавать это приложение поверх Angular.

Angular полезен, когда планируемое приложение имеет высокий уровень интерактивности, особенно если оно представлено пользователю как единое целое. С другой стороны, если сайт состоит из отдельных статических страниц, предназначенных для индивидуального представления поисковым системам, социальным сетям и пользователю, Angular, вероятно, вызовет больше проблем, чем решит.

Автор Дэниел Брук-Роберж