Nieelastyczne podejście do tworzenia aplikacji internetowych na różne urządzenia

Zapytania o multimedia są świetne, ale…

Zapytania o multimedia są świetne i stanowią prawdziwy dar niebiosi dla programistów stron internetowych, którzy chcą wprowadzić drobne zmiany w swoich arkuszach stylów, aby zapewnić lepsze wrażenia użytkownikom na urządzeniach o różnych rozmiarach. Zapytania o multimedia umożliwiają dostosowywanie CSS witryny do rozmiaru ekranu. Zanim zaczniesz czytać ten artykuł, dowiedz się więcej o projektowaniu elastycznym i poznaj kilka przykładów użycia zapytań o media pod adresem mediaqueri.es.

Jak zauważa Brad Frost w poprzednim artykule, zmiana wyglądu to tylko jeden z wielu elementów, które należy wziąć pod uwagę podczas tworzenia witryn mobilnych. Jeśli podczas tworzenia witryny mobilnej jedyne, co robisz, to dostosowywanie układu za pomocą zapytań dotyczących multimediów, mamy taką sytuację:

  • Wszystkie urządzenia otrzymują te same komponenty JavaScript, CSS i zasoby (obrazy, filmy), co powoduje dłuższy niż to konieczne czas wczytywania.
  • Wszystkie urządzenia otrzymują ten sam początkowy DOM, co może zmuszać programistów do pisania zbyt skomplikowanych stylów CSS.
  • ograniczona elastyczność w określaniu niestandardowych interakcji dostosowanych do poszczególnych urządzeń;

Aplikacje internetowe potrzebują więcej niż zapytań medialnych

Nie zrozumcie mnie źle. Nie mam nic przeciwko projektowaniu stron responsywnych za pomocą zapytań o multimedia i zdecydowanie uważam, że ma to swoje miejsce w świecie. Ponadto niektóre z wymienionych wyżej problemów można rozwiązać, stosując obrazy responsywne, dynamiczne wczytywanie skryptów itp. W pewnym momencie możesz jednak zauważyć, że wprowadzasz zbyt wiele drobnych poprawek, i lepiej będzie wyświetlać różne wersje.

Wraz ze wzrostem złożoności interfejsów i przejściem na strony internetowe na jednej stronie będziesz musiał włożyć więcej wysiłku w dostosowywanie interfejsu do każdego typu urządzenia. Z tego artykułu dowiesz się, jak wprowadzić te zmiany z minimalnym nakładem pracy. Ogólne podejście polega na przypisaniu urządzenia użytkownika do odpowiedniej klasy urządzeń i zaprezentowaniu na tym urządzeniu odpowiedniej wersji, przy jednoczesnym maksymalizowaniu ponownego użycia kodu między wersjami.

Na jakie klasy urządzeń kierujesz reklamy?

Istnieje mnóstwo urządzeń połączonych z internetem, a prawie wszystkie z nich mają przeglądarki. Problem polega na ich różnorodności: laptopy Mac, stacje robocze z systemem Windows, iPhone'y, iPady, telefony z Androidem z wyświetlaczem dotykowym, kółka do przewijania, klawiatury, wprowadzanie głosowe, urządzenia reagujące na nacisk, smartwatche, tostery i lodówki oraz wiele innych. Niektóre z tych urządzeń są powszechne, a inne bardzo rzadkie.

na różnych urządzeniach,
Różne urządzenia (source).

Aby zapewnić użytkownikom dobre wrażenia, musisz wiedzieć, kim są Twoi użytkownicy i jakie urządzenia używają. Jeśli tworzysz interfejs użytkownika na potrzeby użytkownika komputera z myszą i klawiaturą, a potem przekazujesz go użytkownikowi smartfona, interfejs będzie dla niego frustrujący, ponieważ jest zaprojektowany pod kątem innego rozmiaru ekranu i innej metody wprowadzania danych.

Istnieją 2 skrajne podejścia:

  1. Utwórz jedną wersję, która będzie działać na wszystkich urządzeniach. W efekcie pogorszy się wrażenia użytkownika, ponieważ różne urządzenia wymagają innego podejścia do projektowania.

  2. Utwórz wersję dla każdego urządzenia, które chcesz obsługiwać. To zajmie wieczność, ponieważ będziesz tworzyć zbyt wiele wersji aplikacji. Ponadto, gdy pojawi się nowy smartfon (co dzieje się mniej więcej co tydzień), będziesz musiał utworzyć kolejną wersję.

Jest to podstawowy kompromis: im więcej kategorii urządzeń masz, tym lepsze wrażenia użytkowników możesz zapewnić, ale tym więcej pracy będziesz musiał włożyć w projektowanie, wdrażanie i utrzymanie.

Tworzenie osobnych wersji dla każdej klasy urządzeń może być dobrym pomysłem ze względu na wydajność lub jeśli wersje, które chcesz wyświetlać na różnych klasach urządzeń, różnią się znacznie. W przeciwnym razie elastyczne projektowanie witryn jest całkiem rozsądnym podejściem.

Możliwe rozwiązanie

Oto kompromis: podziel urządzenia na kategorie i zaprojektuj jak najlepsze wrażenia dla każdej z nich. Wybierane kategorie zależą od produktu i docelowego użytkownika. Oto przykładowa klasyfikacja, która obejmuje popularne obecnie urządzenia z możliwością przeglądania Internetu.

  1. małe ekrany + dotykowe (głównie telefony)
  2. duże ekrany + dotykowe (głównie tablety)
  3. duże ekrany + klawiatura/mysz (głównie komputery stacjonarne/laptopy)

Jest to tylko jeden z wielu możliwych podziałów, ale w momencie pisania tego artykułu ma on szczególny sens. Na liście brakuje urządzeń mobilnych bez ekranów dotykowych (np. telefonów z podstawową przeglądarką, niektórych czytników e-booków). Większość z nich ma jednak zainstalowane oprogramowanie do nawigacji za pomocą klawiatury lub czytnik ekranu, które będzie działać, jeśli zadbasz o dostępność witryny.

Przykłady aplikacji internetowych dostosowanych do konkretnego formatu

Istnieje wiele przykładów usług internetowych, które wyświetlają zupełnie inne wersje dla różnych formuł. Wyszukiwarka Google i Facebook robią to samo. W tym przypadku należy wziąć pod uwagę zarówno wydajność (pobieranie zasobów, renderowanie stron), jak i ogólne wrażenia użytkowników.

W świecie aplikacji natywnych wielu deweloperów decyduje się na dostosowanie interfejsu do klasy urządzenia. Na przykład aplikacja Flipboard na iPada ma zupełnie inny interfejs niż Flipboard na iPhone’a. Wersja na tablet jest zoptymalizowana pod kątem obsługi 2 rękami i przewracania poziomego, a wersja na telefon – pod kątem obsługi jedną ręką i przewracania pionowego. Wiele innych aplikacji na iOS ma też znacznie różniące się wersje na telefon i tablet, np. Things (lista rzeczy do zrobienia) i Showyou (filmy społecznościowe), jak widać poniżej:

znaczne możliwości dostosowywania interfejsu na telefon i tablet;
Ważne zmiany w interfejsie na telefonach i tabletach.

Technika 1. Wykrywanie po stronie serwera

Na serwerze mamy znacznie ograniczone informacje o urządzeniu, z którym mamy do czynienia. Prawdopodobnie najbardziej przydatnym dostępnym źródłem informacji jest ciąg znaków klienta użytkownika, który jest przekazywany w nagłówku User-Agent w każdym żądaniu. Dlatego tutaj też zadziała to samo podejście do wykrywania UA. W istocie, projekty DeviceAtlas i WURFL już to robią (i dodają mnóstwo dodatkowych informacji o urządzeniu).

Niestety każda z nich wiąże się z własnymi wyzwaniami. WURFL jest bardzo duży, zawiera 20 MB pliku XML i może powodować znaczne obciążenie po stronie serwera w przypadku każdego żądania. Niektóre projekty dzielą kod XML ze względu na wydajność. DeviceAtlas nie jest oprogramowaniem open source i wymaga płatnej licencji.

Istnieją też prostsze, bezpłatne alternatywy, takie jak projekt Detect Mobile Browsers. Minusem jest oczywiście to, że wykrywanie urządzeń będzie mniej dokładne. Ponadto rozróżnia tylko urządzenia mobilne i niemobilne, zapewniając ograniczone wsparcie dla tabletów tylko dzięki ad hoc zestawowi poprawek.

Technika 2. Wykrywanie po stronie klienta

Wykorzystując wykrywanie funkcji, możemy dowiedzieć się wielu rzeczy o przeglądarce i urządzeniu użytkownika. Najważniejsze jest to, czy urządzenie ma funkcję dotykowa i czy ma duży czy mały ekran.

Musimy gdzieś wyznaczyć granicę, aby odróżnić małe i duże urządzenia dotykowe. A co z przypadkami szczególnymi, takimi jak Galaxy Note 5? Poniższa grafika przedstawia wiele popularnych urządzeń z Androidem i iOS (z odpowiednimi rozdzielczościami ekranu). Gwiazdka oznacza, że urządzenie jest dostępne lub może być dostępne w podwójnej gęstości. Mimo że gęstość pikseli może być podwojona, CSS nadal podaje te same rozmiary.

Kilka słów o pikselach w CSS: piksele CSS w internecie mobilnym nie są takie same jak piksele na ekranie. Urządzenia z wyświetlaczem Retina w systemie iOS wprowadziły praktykę podwajania gęstości pikseli (np. iPhone 3GS vs. 4, iPad 2 vs. 3). UA w Safari na urządzeniach mobilnych z ekranem Retina nadal raportują tę samą szerokość urządzenia, aby uniknąć problemów z internetem. Inne urządzenia (np. Android) mają wyświetlacze o wyższej rozdzielczości, ponieważ producenci stosują ten sam trik z szerokością urządzenia.

Rozdzielczość urządzenia (w pikselach).
Rozdzielczość urządzenia (w pikselach).

Skomplikowanie tej decyzji wynika jednak z konieczności uwzględnienia zarówno trybu poziomego, jak i pionowy. Nie chcemy wczytywać strony ani dodatkowych skryptów za każdym razem, gdy zmienimy orientację urządzenia, ale chcemy mieć możliwość renderowania strony w inny sposób.

Na poniższym diagramie kwadraty reprezentują maksymalne wymiary każdego urządzenia, które są wynikiem nałożenia konturów orientacji pionowej i poziomej (i uzupełnienia kwadratu):

Rozdzielczość pionowa i pozioma (w pikselach)
Rozdzielczość w orientacji pionowej i poziomej (w pikselach)

Ustawiając wartość progową na 650px, klasyfikujemy iPhone’a i Galaxy Nexus jako małe urządzenia dotykowe, a iPada i Galaxy Tab jako „tablety”. W tym przypadku urządzenie Galaxy Note, które nie ma określonej płci, jest klasyfikowane jako „telefon” i ma układ telefonu.

Rozsądna strategia może wyglądać tak:

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

Zobacz minimalny przykład podejścia do wykrywania cech.

Alternatywnym podejściem jest wykrywanie typu urządzenia na podstawie skanowania klienta. W podstawie tworzysz zestaw heurystyk i porównujesz je z navigator.userAgent użytkownika. Pseudokod wygląda mniej więcej tak:

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

Zobacz przykład podejścia do wykrywania UA.

Uwaga dotycząca wczytywania po stronie klienta

Jeśli na serwerze wykonujesz wykrywanie UA, możesz określić, jakie pliki CSS, JavaScript i DOM mają być dostarczane po otrzymaniu nowej prośby. Jeśli jednak wykrywanie odbywa się po stronie klienta, sytuacja jest bardziej skomplikowana. Masz kilka opcji:

  1. Przekierowanie do adresu URL odpowiedniego dla danego typu urządzenia, który zawiera wersję dla tego typu urządzenia.
  2. dynamicznie wczytywać komponenty dostosowane do typu urządzenia;

Pierwsze podejście jest proste i wymaga przekierowania, np. window.location.href = '/tablet'. Jednak do lokalizacji zostanie dołączona informacja o typie urządzenia, więc warto użyć History API, aby oczyścić adres URL. Niestety to podejście wymaga przekierowania, które może być powolne, zwłaszcza na urządzeniach mobilnych.

Drugie podejście jest nieco bardziej skomplikowane do wdrożenia. Potrzebujesz mechanizmu do dynamicznego wczytywania CSS i JS, a w zależności od przeglądarki możesz nie mieć możliwości wykonywania takich czynności jak dostosowywanie <meta viewport>. Ponieważ nie ma przekierowania, musisz korzystać z pierwotnego kodu HTML, który został przesłany. Oczywiście możesz manipulować nim za pomocą kodu JavaScript, ale może to być powolne lub nieeleganckie, w zależności od aplikacji.

Wybór klienta lub serwera

Oto wady i zalety obu podejść:

Klient Pro:

  • Jest bardziej przyszłościowy, ponieważ opiera się na rozmiarach i możliwościach ekranu, a nie na UA.
  • Nie trzeba stale aktualizować listy UA.

Serwer Pro:

  • Pełna kontrola nad tym, która wersja ma być wyświetlana na danym urządzeniu.
  • Lepsza wydajność: nie trzeba stosować przekierowań na klienta ani wczytywania dynamicznego.

Osobiście wolę zacząć od device.js i wykrywania po stronie klienta. Jeśli w miarę rozwoju aplikacji okaże się, że przekierowanie po stronie klienta znacząco obniża wydajność, możesz łatwo usunąć skrypt device.js i wdrożyć wykrywanie UA na serwerze.

Przedstawiamy device.js

Device.js to punkt wyjścia do semantycznego wykrywania urządzeń na podstawie zapytań medialnych, które nie wymaga specjalnej konfiguracji po stronie serwera. Pozwala to zaoszczędzić czas i siły potrzebne do parsowania ciągu agenta użytkownika.

W tym celu na górze strony <head> podajesz znaczniki z tagami odpowiednimi dla wyszukiwarek (link rel=alternate), aby wskazać, które wersje witryny chcesz udostępnić.

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

Następnie możesz samodzielnie wykrywać UA po stronie serwera i przekierowywać wersje lub użyć skryptu device.js do przekierowania po stronie klienta na podstawie funkcji.

Więcej informacji znajdziesz na stronie projektu device.js oraz w udanej aplikacji, która używa biblioteki device.js do przekierowania po stronie klienta.

Zalecenie: MVC z widokami zależnymi od formatu

Zapewne myślisz teraz, że sugeruję Ci stworzenie 3 zupełnie oddzielnych aplikacji, po jednej dla każdego typu urządzenia. Nie! Udostępnianie kodu jest kluczowe.

Mamy nadzieję, że używasz frameworku podobnego do MVC, np. Backbone czy Ember. Jeśli tak, znasz już zasadę rozdzielenia zadań, zgodnie z którą interfejs użytkownika (warstwa widoku) powinien być odseparowany od logiki (warstwa modelu). Jeśli nie znasz tego podejścia, zacznij od zapoznania się z tymi zasobami na temat MVCMVC w JavaScriptzie.

Model działania na różnych urządzeniach doskonale pasuje do Twojego obecnego frameworka MVC. Możesz łatwo przenosić widoki do osobnych plików, tworząc widok niestandardowy dla każdego typu urządzenia. Następnie możesz wyświetlać ten sam kod na wszystkich urządzeniach, z wyjątkiem warstwy widoku.

Model MVC na różnych urządzeniach.
MVC na różnych urządzeniach.

Twój projekt może mieć taką strukturę (możesz oczywiście wybrać strukturę, która najlepiej pasuje do Twojej aplikacji):

models/ (modele wspólne) item.js item-collection.js

controllers/ (shared controllers) item-controller.js

versions/ (elementy związane z urządzeniem) tablet/ desktop/ phone/ (kod dla telefonu) style.css index.html views/ item.js item-list.js

Taka struktura umożliwia pełną kontrolę nad tym, jakie komponenty wczytuje każda wersja, ponieważ masz niestandardowy kod HTML, CSS i JavaScript dla każdego urządzenia. Jest to bardzo skuteczne i może prowadzić do najbardziej efektywnego i wydajnego tworzenia aplikacji internetowych na wiele urządzeń bez stosowania sztuczek takich jak obrazy adaptacyjne.

Po uruchomieniu ulubionego narzędzia do kompilacji połączysz i zminifikujesz wszystkie pliki JavaScript i CSS w pojedyncze pliki, aby szybciej je wczytywać. Gotowy kod HTML będzie wyglądać mniej więcej tak (w przypadku telefonu, z użyciem pliku 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>

Zapytanie o media (touch-enabled: 0) jest niestandardowe (wdrożone tylko w Firefoksie za pomocą prefiksu dostawcy moz), ale jest obsługiwane prawidłowo (dzięki Modernizr.touch) przez device.js.

Zastąpienie wersji

Wykrywanie urządzenia może czasami nie działać prawidłowo, a w niektórych przypadkach użytkownik może preferować układ tabletu na telefonie (np. Galaxy Note). Dlatego ważne jest, aby dać użytkownikom możliwość wyboru wersji witryny, której mają użyć, jeśli chcą ręcznie zmienić wersję.

Zwykle w wersji mobilnej podaje się link do wersji na komputery. Jest to dość łatwe do wdrożenia, ale biblioteka device.js obsługuje tę funkcję za pomocą parametru device GET.

Wnioski

Podsumowując, podczas tworzenia interfejsów jednostronicowych na wiele urządzeń, które nie pasują do świata projektowania elastycznego, wykonaj te czynności:

  1. Wybierz zestaw klas urządzeń, które chcesz obsługiwać, oraz kryteria, według których urządzenia mają być klasyfikowane.
  2. Stwórz aplikację MVC z silną separacją funkcji, oddzielając widoki od reszty kodu źródłowego.
  3. Użyj pliku device.js do wykrywania klasy urządzenia po stronie klienta.
  4. Gdy wszystko będzie gotowe, zapakuj skrypt i arkusz stylów do jednej z klas dla każdego urządzenia.
  5. Jeśli wydajność przekierowania po stronie klienta jest problemowa, zrezygnuj z użycia biblioteki device.js i przejdź na wykrywanie UA po stronie serwera.