- PVSM.RU - https://www.pvsm.ru -
На сегодняшний день существует немалое количество обзорных статей о PhoneGap, но к сожалению, написаны они или front-end разработчиками, которые решили заняться мобильными платформами, или нативными программистами, которые решили попробовать себя в кроссплатформенной разработке. И именно с этих позиций рассматриваются достоинства и недостатки PhoneGap'а, возникают статьи о том, «насколько крута кроссплатформа», или об «ущербности кроссплатформенных решений».
В качестве затравки — видео демо-приложения, написанного за 6 часов; готовым был взят UI-бутстрап, наверстанный за 3,5 часа; использовались библиотеки iScroll, backbone, underscore, Jquery, и небольшая обертка на backbone (RAD.js — rapid application development, архитектурный фреймворк, берущий на себя часть оптимизации, связанной с мобильной средой выполнения).
Еще 2 часа было потрачено на фикс движка. Но сегодня речь не о том, что что-то тормозит, дергается, или самописный свайп не всегда вовремя отрабатывает на 14000 объектах данных; речь о том, что на PhoneGap можно и нужно писать.
Выносим на Ваше рассмотрение мнение людей, которые занимаются кроссплатформеной разработкой на PhoneGap, дабы рассказать о том, какие тонкости мы находим нужными и важными при разработке на PhoneGap, и почему они так важны.
PhoneGap позиционируется как платформа для кроссплатформенной разработки на HTML/JavaScript под семь мобильных операционных платформ [1].
Кроссплатформенность в PhoneGap достигается тем, что код Вашего HTML/JavaScript-приложения выполняется в компоненте webView (представляющего собой, встроенный в приложение браузер).
Фактически разработка PhoneGap-приложения представляет собой разработку HTML5-приложения с учетом особенностей среды выполнения (мобильное устройство, ограниченная процессорная мощность, память, тачскрин и т.д.) и… зоопарка браузеров.
Точно так, как при разработке под десктопные браузеры, при разработке мобильных сайтов или PhoneGap-приложений Вам необходимо знать и учитывать специфику каждой операционной системы и ее дефолтного браузера, на базе которого построен компонент webView.
И главное, на что влияет ограниченность мобильной среды выполнения PhoneGap-приложения и ресурсов, — это отзывчивость пользовательского интерфейса.
Для рядового пользователя не страшно, если на время выполнения каких-либо длительных операций программа заблокирует интерфейс, показав, к примеру, диалог с прогрессом выполнения задачи. Намного хуже, если пользователь подсознательно испытает дискомфорт, когда при нажатии кнопка отреагирует не сразу. Речь в данном случае идет даже не о секундах; человеку для ощущения подсознательного дискомфорта достаточно задержки более 150 миллисекунд между нажатием и реакцией программы. В этом случае наше подсознание просигнализирует о том, что интерфейс программы реагирует не так, как мы ожидаем.
Поэтому мы в своих программах пытаемся акцентировать внимание на отзывчивости пользовательского интерфейса. И в данный статье не будем говорить об оптимизации JavaScript-кода или CSS, так как это не специфично для PhoneGap и мобильных приложений, и, кроме этого, об этом уже немало написано(тут [2], тут [3], тут [4] или тут [5]).
Нам бы хотелось, наоборот, сделать акцент на специфике и тонкостях, на которые часто не обращают внимания, а в итоге это начинает играть решающую роль для пользователя.
Статьи, которые комплексно рассматривают взаимосвязь интерфейса и отзывчивости мобильного веб-приложения, найти непросто. Для примера можно привести эту статью [6], или же эту [7]. Вторая статья достаточно свежая, и указывает направление «движения» для создания хорошего приложения на PhoneGap, но автор в ряде случаев использует «canvas based UI», что не всегда возможно).
Но и в этих статьях, к сожалению, рассмотрены не все проблемы.
Выделим проблемы, которые мы считаем характерными, и которые хотели бы рассмотреть сегодня:
Возможно в чем-то мы повторимся, но всеже начнем.
Проблема задержки в 300 мсек:
Большинство мобильных операционных систем в своем браузере (и как следствие, в webView) для решения проблемы ложных касаний вводят специальную задержку около 300 миллисекунд между непосредственным нажатием и запуском события «click» в DOM'e.
Подход к решению этой проблемы нетрудно найти на просторах интернета. Существует как минимум несколько десятков JavaScript-библиотек, решающих эту проблему. Принцип действия всех их одинаков — отслеживать события «touchstart» и «touchend» — и в момент окончания последнего вызывать событие «click» или аналогичное кастомное (например, «tap»). Примеры можно найти и как решение на stackoverflow [8], и как плагин к JQuery [9], и как отдельную библиотеку [10], причем даже в реализации от Mozilla [11] или просто в качестве советов [12].
К сожалению, всегда есть небольшое «но» — если генерировать событие «click» (например, как советует компания google в своем решении [13]), то через некоторую задержку Вам придется отловить и прекратить распространение стандартного «click», сгенерированного самим браузером. Что в старых версиях android может привести к несовместимости со сторонними библиотеками. При этом, если генерировать кастомное событие, то фокус будут передаваться с задержкой, и эта задержка будет видна при выборе полей ввода.
Найти же библиотеку, которая корректно передает и устанавливает фокусы при клике для элементов, и в то же время корректно работает с различными типами полей ввода, достаточно трудно и впридачу она будет довольно объемной. Намного проще и безболезненней использовать общепринятый подход — генерировать не событие «click», а другое любое кастомное событие — и подвязывать слушателя непосредственно к нему.
И хотя, в этом случае Ваше приложение и становится зависимым от данной библиотеки, этот недостаток с лихвой перекрывается простотой решения и наименьшим количеством багов.
Следует также предостеречь Вас от привязки отдельных экземпляров «fastclick» на каждый элемент UI, так как делают некоторые библиотеки. Это решение, хоть и работоспособное, но крайне затратное с точки зрения потребления ресурсов мобильного устройства. Необходимо использовать делегирование и привязку, например, на «body» слушателя и генерацию всплывающего кастомного события по координатам нативного.
Вторая проблема — точка касания:
Еще одна часто встречающаяся проблема в неправильно спроектированных интерфейсах мобильных устройств (в том числе и на PhoneGap) звучит из уст пользователей так: «я нажимаю, а оно не всегда срабатывает» или «я нажимаю, а оно не работает».
Давайте внимательно рассмотрим следующий рисунок, отображающий, как именно происходит взаимодействие пальца пользователя с экраном устройства.
Несложно заметить, что точка реального контакта подушечки пальца (зеленая) и точка визуального контакта (красная) отличаются. Во-первых, мы, как правило, смотрим на мобильное устройство под углом. Во-вторых, сам экран генерирует координаты касания как средневесовые координаты по площади касания подушечки пальца. Как бы мы ни старались, идеального круга от подушечки пальцев у нас не получится.
Именно по этим причинам, реальная точка касания никогда не совпадет с визуальной.
Большую часть решения этих проблем берет на себя операционная система, а именно, непосредственно сам драйвер тачскрина.
Но если не знать этих тонкостей, и не учитывать их при разработке интерфейса программы, то при достаточно мелких деталях интерфейса это становится для пользователя приложения ощутимой проблемой — нажатие происходит не всегда. И хотя пользователь абсолютно уверен, что он нажал точно на кнопку, реально же он просто нажал мимо.
Исправляется этот момент достаточно просто — грамотной версткой элементов управления приложения; например, когда кнопка имеет невидимый падинг в 4-6 пикселей если она недостаточно большая по дизайну: то есть ее бэкграунд имеет невидимые внешние границы, которые увеличивают ее физический размер до необходимого и удобного пользователю (розовое поле на рисунке).
Очень подробно эта проблема рассмотрена в здесь [14], а в рекомендациях компании Microsoft [15] можно увидеть рекомендуемые размеры элементов интерфейса, в зависимости от размера экрана.
А теперь о самом нетривиальном — об оптимизации DOM-структуры
Если посмотреть на более-менее «стандартный» экран «среднего» приложения, то можно насчитать от 200-300 html тегов (узлов DOM), с помощью которых наверстан экран приложения. В данном конкретном примере страница наверстана с помощью 225 HTML-элементов. Далеко, не самая оптимальная верстка, но, как бы мы ни старались, в любом случае, в среднем экране PhoneGap-приложения можно насчитать от 100 до 250 элементов HTML. И попросим учесть, что мы пока не рассматриваем экраны со списками (об этом поговорим чуть-чуть позже).
Среднее приложение, аналогичное нативному, содержит от пяти до полутора десятков экранов. В случае PhoneGap, все эти старницы являются частью single-page приложения.
Существует общепринятый подход к архитектуре такого приложения: все страницы верстаются в одном html файле как блоки, а потом, в ходе выполнения программы, в зависимости от того, какая страница необходима, стилями
display: none;
display: block;
отображается нужная страница (так, например, делается в JqueryMobile ).
Но этот подход, к сожалению, приводит к задержкам в приложении при переключении страниц(и не только). И чем сложнее приложение, тем длительнее эти задержки.
Связано это с тем, что даже для самой обычной JavaScript-функции запроса к DOM, например, document.querySelector('selector'), время выполнения зависит от количества узлов в объектной модели документа. К примеру, указанная операция при трехуровневом селекторе выполняется за 0.003 мсек при 100 элементах и 0.36 мсек при 10000 элементах (ориентировочно 40 страниц приложения). Можно возразить, что это мелочи: 40 страниц не часто встречаются в реальной жизни, да и 0.36 мсек — копейки.
Но чистым JavaScript никто никогда практически не пользуется, а при использовании JQuery(до 2 версии) указанное время вырастает до 2.46 мс. А если учитывать, что кроме простого запроса необходимо также провести какие-либо манипуляции с DOM, то это уже может привести к заметным даже на глаз задержкам в достаточно сложном приложении.
Приведем небольшой СИНТЕТИЧЕСКИЙ тест [16], генерирующий документ с различным количеством элементов, и выводящий результат выборки двухуровнего CSS в консоль.
Результат его выполнения на Chrome 28.0.1500.63
start create 1 items index.js:12
none 1 items: 0.078ms index.js:38
none jquery 1 items: 0.774ms index.js:43
block 1 items: 0.131ms index.js:38
block jquery 1 items: 0.234ms index.js:43
jquery show1 items: 1.768ms index.js:52
start create 10 items index.js:12
none 10 items: 0.071ms index.js:38
none jquery 10 items: 0.138ms index.js:43
block 10 items: 0.139ms index.js:38
block jquery 10 items: 0.090ms index.js:43
jquery show10 items: 0.865ms index.js:52
start create 100 items index.js:12
none 100 items: 0.071ms index.js:38
none jquery 100 items: 0.099ms index.js:43
block 100 items: 0.103ms index.js:38
block jquery 100 items: 0.105ms index.js:43
jquery show100 items: 1.418ms index.js:52
start create 1000 items index.js:12
none 1000 items: 0.178ms index.js:38
none jquery 1000 items: 0.163ms index.js:43
block 1000 items: 0.156ms index.js:38
block jquery 1000 items: 0.176ms index.js:43
jquery show1000 items: 9.709ms index.js:52
start create 10000 items index.js:12
none 10000 items: 2.333ms index.js:38
none jquery 10000 items: 2.434ms index.js:43
block 10000 items: 1.810ms index.js:38
block jquery 10000 items: 1.810ms index.js:43
jquery show10000 items: 106.425ms
Как видно из теста, решение с «display: none» не помогает, так как это убирает работу только по отрисовке документа, но не меняет времени при запросах к «избыточному» DOM.
Это решение помогает нам избавиться только от reflow и repaint, поведение браузера при этих событиях великолепно рассмотрено в статье [17].
Еще раз напоминаем, что тест синтетический, не соответствующий реальным условиям, и некоторые позиции были специально «завалены». Но он прекрасно дает представление, что может произойти в реальном проекте с single-page приложением при большом количестве страниц и сложной верстке.
И хотя, время будет различаться на различных платформах (в различных браузерах), нетрудно увидить, что в мобильных браузерах ситуация будет еще хуже — в сложных одностраничных приложениях со стандартным подходом вы вряд ли получите плавную анимацию на мобильном устройстве.
В этом случае есть два варианта решения:
Максимальной же оптимизации можно добиться, комбинируя эти два варианта. Тогда загружать страницу или шаблон Вам прийдется только в первый раз, в дальнейшем просто присоединяя или отсоединяя DOM ветви к основному дереву документа.
Не следует также забывать и об известных местах оптимизации [19].
И снова о DOM — проблема больших списков
Проблема больших списков является частью проблемы описанной в предыдущем разделе.
Чем больше элементов списка вы имеете, тем больше элементов HTML будет в странице, и тем медленнее будет происходить обновление и отрисовка списка.
В нативных компонентах это реализовано через кэш (пример реализации нативного компонента на Android [20]).
Проблему можно решить или заменой данных в уже скрытом элементе и также перестановкой его позиции или удалением скрытого элемента списка и генерации нового. То есть, фактически на экране существует не список из 1000 элементов, а список из 10 видимых на экране элементов, и возможно несколько скрытых элементов, с которыми в данный момент происходит работа.
В веб-приложениях такую реализацию списков практически невозможно увидеть; как правило, все делается максимально просто: список из 1000 элементов. И если на десктопе это не настолько критично, то на мобильных платформах большие списки могут сыграть решающую роль в отказе от PhoneGap как платформы разработки.
В качестве решения данной проблемы можно порекомендовать «пейджинацию», то есть отображение списка по частям. Данное архитектурное решение реализуют множество js-библиотек; его можно очень красиво обыграть — например, свайпом.
Или же можно написать или найти JavaScript-библиотеку, реализующую функциональность аналогичную нативным компонентам; например, как сделано в InfiniteWall [21] или здесь [22].
Напоследок хотелось бы дать несколько полезных советов, которые, как мы надеемся, помогут Вам сэкономить время и нервы:
transform: translate3d(0,0,0);
-webkit-transform: translate3d(0,0,0);
И еще несколько ссылок на статьи с советами: тут [25], тут [26] или тут [27].
Но самое главное, что мы хотим сказать: На PhoneGap'e можно писать.
Хотя он не является панацеей для разработки. Каждый инструмент предназначен для своих задач. С помощью PhoneGap можно написать отзывчивое приложение и/или приложение с нестандартным дизайном (причем это даже легче сделать, чем при реализации нативным кодом). Но если Вам нужна обработка больших объемов данных, то скорее всего, PhoneGap Вам не подойдет, хотя иногда и с этим можно бороться.
Надеемся наши советы пригодятся.
P.S. Спустя 11 часов оптимизации UI баги остались, с теми же размерами кнопок, но приложению реально «полегчало».
Автор: YuriyLuchaninov
Источник [28]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/mobil-ny-e-prilozheniya/39700
Ссылки в тексте:
[1] под семь мобильных операционных платформ: http://phonegap.com/about/feature/
[2] тут: http://kellegous.com/j/2013/01/26/layout-performance/
[3] тут: http://www.igvita.com/2013/01/15/faster-websites-crash-course-on-web-performance/
[4] тут: http://web-standards.ru/articles/front-end-performance/
[5] тут: http://habrahabr.ru/post/124203/
[6] эту статью: http://cheeaun.com/blog/2012/03/how-i-built-hacker-news-mobile-web-app
[7] эту: http://aerotwist.com/blog/making-a-60fps-mobile-app/
[8] stackoverflow: http://stackoverflow.com/questions/8599227/javascript-disable-second-onclick
[9] JQuery: https://github.com/dave1010/jquery-fast-click/blob/master/jQuery.fastClick.js
[10] библиотеку: http://labs.ft.com/2011/08/fastclick-native-like-tapping-for-touch-apps/
[11] Mozilla: https://github.com/mozilla/pointer.js/blob/master/pointer.js
[12] советов: http://cubiq.org/remove-onclick-delay-on-webkit-for-iphone
[13] google в своем решении: https://developers.google.com/mobile/articles/fast_buttons?hl=ru-RU
[14] здесь: http://www.uxmatters.com/mt/archives/2013/03/common-misconceptions-about-touch.php
[15] Microsoft: http://download.microsoft.com/download/8/A/6/8A652B51-AF09-4A5A-888C-A0465D00FE5E/Windows%208%20Touch%20Guidance.pdf
[16] СИНТЕТИЧЕСКИЙ тест: http://jsfiddle.net/2mKmW/
[17] статье: http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
[18] api.jquery.com/detach/: http://api.jquery.com/detach/
[19] местах оптимизации: http://www.ozon.ru/context/detail/id/18421547/
[20] пример реализации нативного компонента на Android: http://android.amberfog.com/?p=296
[21] InfiniteWall: http://cubiq.org/infiniwall
[22] здесь: http://w2ui.com/web/blog/7/JavaScript-Grid-with-One-Million-Records
[23] конкретной реализации: http://love-media.net/articles/ipad-animation
[24] более подробно с тестами: http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
[25] тут: http://www.tricedesigns.com/2012/01/17/mobile-web-phonegap-html-dev-tips/
[26] тут: http://developer.android.com/guide/webapps/targeting.html
[27] тут: https://developers.google.com/mobile/articles/webapp_fixed_ui
[28] Источник: http://habrahabr.ru/post/187960/
Нажмите здесь для печати.