Неадаптивный подход к созданию кросс-девайсных веб-приложений

Борис Смус
Boris Smus

Медиа-запросы — это здорово, но…

Медиазапросы — это просто находка для разработчиков веб-сайтов, которые хотят внести небольшие изменения в таблицы стилей, чтобы улучшить взаимодействие с пользователями на устройствах разных размеров. По сути, медиазапросы позволяют настраивать CSS вашего сайта в зависимости от размера экрана. Прежде чем погрузиться в эту статью, узнайте больше об адаптивном дизайне и ознакомьтесь с прекрасными примерами использования медиазапросов здесь: mediaqueri.es .

Как отметил Брэд Фрост в предыдущей статье , изменение внешнего вида — лишь один из многих факторов, которые следует учитывать при разработке сайта для мобильного интернета. Если при разработке мобильного сайта вы ограничиваетесь лишь настройкой макета с помощью медиазапросов, то ситуация выглядит следующим образом:

  • Все устройства используют одинаковые JavaScript, CSS и ресурсы (изображения, видео), что приводит к более длительной, чем необходимо, загрузке.
  • Все устройства получают один и тот же начальный DOM, что потенциально вынуждает разработчиков писать слишком сложный CSS.
  • Ограниченная гибкость при настройке индивидуальных взаимодействий для каждого устройства.

Веб-приложениям нужно больше, чем просто медиа-запросы

Не поймите меня неправильно. Я не против адаптивного дизайна с использованием медиазапросов и определённо считаю, что ему есть место в мире. Более того, некоторые из вышеупомянутых проблем можно решить с помощью таких подходов, как адаптивные изображения , динамическая загрузка скриптов и т. д. Однако в какой-то момент вы можете обнаружить, что вносите слишком много постепенных изменений, и, возможно, лучше использовать разные версии.

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

На какие классы устройств вы ориентируетесь?

Существует множество устройств, подключенных к интернету, и почти у всех есть браузеры. Сложность заключается в их разнообразии: ноутбуки Mac, рабочие станции Windows, iPhone, iPad, телефоны Android с сенсорным вводом, колесами прокрутки, клавиатурами, голосовым вводом, устройства с чувствительностью к нажатию, умные часы, тостеры и холодильники и многое другое. Некоторые из этих устройств встречаются повсеместно, другие же встречаются крайне редко.

Разнообразие устройств.
Разнообразие устройств ( источник ).

Чтобы создать хороший пользовательский опыт, необходимо знать, кто ваши пользователи и какие устройства они используют. Если вы создадите интерфейс для пользователя настольного компьютера с мышью и клавиатурой, а затем перенесёте его на смартфон, он будет вызывать дискомфорт, поскольку он рассчитан на другой размер экрана и другую модальность ввода.

Существует два крайних подхода:

  1. Создайте одну версию, работающую на всех устройствах. В результате пострадает пользовательский опыт (UX), поскольку для разных устройств требуются разные решения по дизайну.

  2. Создайте версию для каждого устройства, которое вы хотите поддерживать. Это займёт целую вечность, поскольку вам придётся создавать слишком много версий приложения. Кроме того, когда появится новый смартфон (что происходит примерно еженедельно), вам придётся создавать ещё одну версию.

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

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

Потенциальное решение

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

  1. маленькие экраны + сенсорные (в основном телефоны)
  2. большие экраны + сенсорные (в основном планшеты)
  3. большие экраны + клавиатура/мышь (в основном настольные компьютеры/ноутбуки)

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

Примеры веб-приложений, специфичных для определенного форм-фактора

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

В мире нативных приложений многие разработчики предпочитают адаптировать свой интерфейс к классу устройств. Например, пользовательский интерфейс Flipboard для iPad существенно отличается от интерфейса Flipboard для iPhone. Версия для планшета оптимизирована для работы двумя руками и горизонтального перелистывания, в то время как версия для телефона предназначена для взаимодействия одной рукой и вертикального перелистывания. Многие другие приложения для iOS также предлагают существенно отличающиеся версии для телефонов и планшетов, например, Things (список дел) и Showyou (социальные видео), представленные ниже:

Значительная настройка пользовательского интерфейса для телефонов и планшетов.
Значительная настройка пользовательского интерфейса для телефонов и планшетов.

Подход №1: обнаружение на стороне сервера

На сервере мы имеем гораздо более ограниченное представление об устройстве, с которым имеем дело. Вероятно, самая полезная доступная подсказка — это строка пользовательского агента, которая передается через заголовок User-Agent в каждом запросе. Поэтому тот же подход с прослушиванием UA будет работать и здесь. Фактически, проекты DeviceAtlas и WURFL уже это делают (и предоставляют массу дополнительной информации об устройстве).

К сожалению, каждый из этих подходов имеет свои сложности. WURFL очень большой, содержит 20 МБ XML-данных, что потенциально приводит к значительным накладным расходам на стороне сервера при каждом запросе. Некоторые проекты разделяют XML-данные для повышения производительности. DeviceAtlas не имеет открытого исходного кода и требует платной лицензии для использования.

Существуют и более простые бесплатные альтернативы, например, проект Detect Mobile Browsers . Недостаток, конечно, в том, что обнаружение устройств неизбежно будет менее полным. Кроме того, он различает только мобильные и стационарные устройства, обеспечивая ограниченную поддержку планшетов только с помощью специального набора настроек .

Подход №2: обнаружение на стороне клиента

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

Нам нужно провести границу между маленькими и большими сенсорными устройствами. А как насчёт крайних случаев, таких как 5-дюймовый Galaxy Note? На следующем рисунке показан ряд популярных устройств Android и iOS, наложенных друг на друга (с соответствующими разрешениями экрана). Звёздочка означает, что устройство поставляется или может поставляться с удвоенной плотностью пикселей. Хотя плотность пикселей может быть удвоена, CSS по-прежнему отображает те же размеры.

Небольшое отступление о пикселях в CSS: пиксели CSS в мобильном интернете отличаются от пикселей на экране. На устройствах iOS с Retina-дисплеями появилась практика удвоения плотности пикселей (например, iPhone 3GS против 4, iPad 2 против 3). Пользовательские агенты Safari для мобильных устройств с Retina-дисплеями по-прежнему используют ту же ширину экрана, чтобы избежать разрывов. По мере того, как другие устройства (например, Android) получают дисплеи с более высоким разрешением, они используют тот же трюк с шириной экрана.

Разрешение устройства (в пикселях).
Разрешение устройства (в пикселях).

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

На следующей диаграмме квадраты представляют максимальные размеры каждого устройства, полученные в результате наложения портретной и альбомной ориентаций (и заполнения квадрата):

Разрешение портретной и альбомной ориентации (в пикселях)
Разрешение портретной и альбомной ориентации (в пикселях)

Установив пороговое значение 650px , мы классифицируем iPhone и Galaxy Nexus как «маленькие сенсорные», а iPad и Galaxy Tab — как «планшеты». Андрогинный Galaxy Note в этом случае классифицируется как «телефон» и получит телефонную раскладку.

Итак, разумная стратегия может выглядеть так:

if (hasTouch) {
  if (isSmall) {
    device = PHONE;
  } else {
    device = TABLET;
  }
} else {
  device = DESKTOP;
}

Посмотрите на минимальный пример подхода к обнаружению признаков в действии.

Альтернативный подход — использовать анализ UA для определения типа устройства. По сути, вы создаёте набор эвристик и сопоставляете их с navigator.userAgent вашего пользователя. Псевдокод выглядит примерно так:

var ua = navigator.userAgent;
for (var re in RULES) {
  if (ua.match(re)) {
    device = RULES[re];
    return;
  }
}

Посмотрите пример подхода к обнаружению UA в действии.

Примечание о загрузке на стороне клиента

Если вы используете обнаружение UA на сервере, вы можете выбрать, какие CSS, JavaScript и DOM-коды обрабатывать при получении нового запроса. Однако, если вы используете обнаружение на стороне клиента, ситуация становится сложнее. У вас есть несколько вариантов:

  1. Перенаправление на URL-адрес, соответствующий типу устройства, который содержит версию для этого типа устройства.
  2. Динамически загружайте ресурсы, специфичные для типа устройства.

Первый подход прост и требует перенаправления, например, window.location.href = '/tablet' . Однако теперь к местоположению будет добавлена ​​информация о типе устройства, поэтому вам может потребоваться использовать API истории для очистки URL. К сожалению, этот подход подразумевает перенаправление, которое может быть медленным, особенно на мобильных устройствах.

Второй подход реализовать гораздо сложнее. Вам потребуется механизм динамической загрузки CSS и JavaScript, и (в зависимости от браузера) вы можете не иметь возможности настраивать <meta viewport> . Кроме того, поскольку перенаправления нет, вы ограничены исходным HTML-кодом, который был предоставлен. Конечно, вы можете управлять им с помощью JavaScript, но это может быть медленно и/или неэлегантно, в зависимости от вашего приложения.

Выбор клиента или сервера

Вот компромиссы между подходами:

Профессиональный клиент :

  • Больше перспектив на будущее, поскольку основано на размерах экрана и его возможностях, а не на пользовательском потенциале.
  • Нет необходимости постоянно обновлять список UA.

Профессиональный сервер :

  • Полный контроль над тем, какую версию предоставлять тем или иным устройствам.
  • Лучшая производительность: нет необходимости в перенаправлениях клиентов или динамической загрузке.

Лично я предпочитаю начинать с device.js и обнаружения на стороне клиента. Если по мере развития вашего приложения перенаправление на стороне клиента станет серьёзным недостатком производительности, вы можете легко удалить скрипт device.js и реализовать обнаружение UA на сервере.

Представляем device.js

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

Идея заключается в том, что вы предоставляете удобную для поисковых систем разметку ( link rel=alternate ) в верхней части <head> указывающую, какие версии вашего сайта вы хотите предоставить.

<link rel="alternate" href="http://foo.com" id="desktop"
    media="only screen and (touch-enabled: 0)">

Далее вы можете либо выполнить обнаружение UA на стороне сервера и самостоятельно управлять перенаправлением версий, либо использовать скрипт device.js для выполнения перенаправления на стороне клиента на основе функций.

Более подробную информацию можно найти на странице проекта device.js , а также на фейковом приложении , которое использует device.js для перенаправления на стороне клиента.

Рекомендация: MVC с представлениями, специфичными для форм-фактора

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

Надеюсь, вы уже использовали фреймворк, подобный MVC, например, Backbone, Ember и т. д. Если да, то вы знакомы с принципом разделения ответственности, в частности, с тем, что пользовательский интерфейс (уровень представления) должен быть отделен от логики (уровня модели). Если это для вас в новинку, ознакомьтесь с этими материалами по MVC и MVC в JavaScript .

Кросс-девайсная история идеально вписывается в вашу существующую структуру MVC. Вы можете легко перенести представления в отдельные файлы, создав индивидуальное представление для каждого типа устройства. Тогда вы сможете использовать один и тот же код на всех устройствах, за исключением уровня представления.

Кросс-девайс MVC.
Кросс-девайс MVC.

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

models/ (общие модели) item.js item-collection.js

controllers/ (общие контроллеры) item-controller.js

versions/ (специфичные для устройства данные) tablet/ desktop/ phone/ (специфичный для телефона код) style.css index.html views/ item.js item-list.js

Такая структура позволяет полностью контролировать, какие ресурсы загружает каждая версия, поскольку для каждого устройства используются собственные HTML, CSS и JavaScript. Это очень эффективно и может привести к самому экономичному и производительному способу разработки для кросс-девайсного веба, без использования таких трюков, как адаптивные изображения.

После запуска вашего любимого инструмента сборки вы объедините и минимизируете все ваши JavaScript и CSS в отдельные файлы для более быстрой загрузки, при этом ваш рабочий HTML-код будет выглядеть примерно так (для телефона с использованием device.js):

<!doctype html>
<head>
  <title>Mobile Web Rocks! (Phone Edition)</title>

  <!-- Every version of your webapp should include a list of all
        versions. -->
  <link rel="alternate" href="http://foo.com" id="desktop"
      media="only screen and (touch-enabled: 0)">
  <link rel="alternate" href="http://m.foo.com" id="phone"
      media="only screen and (max-device-width: 650px)">
  <link rel="alternate" href="http://tablet.foo.com" id="tablet"
      media="only screen and (min-device-width: 650px)">

  <!-- Viewport is very important, since it affects results of media
        query matching. -->
  <meta name="viewport" content="width=device-width">

  <!-- Include device.js in each version for redirection. -->
  <script src="device.js"></script>

  <link rel="style" href="phone.min.css">
</head>
<body>
  <script src="phone.min.js"></script>
</body>

Обратите внимание, что медиа-запрос (touch-enabled: 0) нестандартен (реализован только в Firefox с префиксом поставщика moz ), но обрабатывается правильно (благодаря Modernizr.touch ) с помощью device.js.

Переопределение версии

Иногда обнаружение устройства может давать сбои, а в некоторых случаях пользователь может предпочесть просматривать макет планшета на своем телефоне (возможно, он использует Galaxy Note), поэтому важно предоставить пользователям возможность выбора, какую версию вашего сайта использовать, если они хотят вручную переопределить.

Обычный подход — предоставить ссылку на десктопную версию из мобильной. Это достаточно просто реализовать, но device.js поддерживает эту функциональность с помощью GET-параметра device .

Заключение

Подводя итог, при создании кросс-девайсных одностраничных пользовательских интерфейсов, которые не вписываются в мир адаптивного дизайна, сделайте следующее:

  1. Выберите набор классов устройств для поддержки и критерии, по которым устройства будут классифицироваться по классам.
  2. Создавайте приложение MVC с четким разделением задач, отделяя представления от остальной кодовой базы.
  3. Используйте device.js для определения класса устройства на стороне клиента.
  4. Когда будете готовы, упакуйте свой скрипт и таблицы стилей в один экземпляр каждого класса устройства.
  5. Если производительность перенаправления на стороне клиента вызывает проблемы, откажитесь от device.js и переключитесь на обнаружение UA на стороне сервера.