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

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

Медиа-запросы — это потрясающая вещь, находка для разработчиков веб-сайтов, которые хотят внести небольшие изменения в свои таблицы стилей, чтобы обеспечить удобство работы пользователей на устройствах разных размеров. Медиа-запросы, по сути, позволяют вам настроить 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). UA Retina Mobile Safari по-прежнему сообщают об одной и той же ширине устройства, чтобы не нарушать работу Интернета. Поскольку другие устройства (например, Android) получают дисплеи с более высоким разрешением, они проделывают тот же трюк с шириной устройства.

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

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

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

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

Установив пороговое значение 650px , мы классифицируем iPhone, Galaxy Nexus как smalltouch, а 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' . Однако теперь к местоположению будет добавлена ​​информация об этом типе устройства, поэтому вы можете использовать History API для очистки вашего URL-адреса. К сожалению, этот подход предполагает перенаправление, которое может быть медленным, особенно на мобильных устройствах.

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

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

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

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

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

Про сервер :

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

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

Представляем устройство.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.

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

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

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

версии/ (данные, специфичные для устройства) планшет/ настольный компьютер/ телефон/ (код для конкретного телефона) style.css index.htmlviews/ 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-обнаружение на стороне сервера.