Содержание статьи
Известный факт: далеко не всегда надо лезть в дебри нативных технологий, чтобы создать функциональное приложение для популярных мобильных платформ. Проверенный временем PhoneGap давно научился эффективно переносить JavaScript-код в нестандартную среду выполнения. Для полного счастья не хватает только рюшечек в нативном стиле, а ими готов поделиться замечательный фреймворк Framework7.
Objective-C, Swift или JavaScript?
Скажу прямо, ваш покорный слуга тащится от многих продуктов яблочной компании... но не от Objective-C. У меня было несколько попыток с ним подружиться, но отношения как-то не развивались. Ну не нравится он мне, и все. Релиз Swift, можно сказать, исправил ситуацию, но пока он в стадии активной разработки и шлифовки. Применять его в реальных проектах не сильно хочется.
Поразмышляв о плюсах и минусах всех доступных технологий разработки под iOS, я решил остановиться на своем любимом JavaScript. Если нет жизненной необходимости в нативных фишках Objective-C, а за плечами имеется опыт разработки на JavaScript, то почему бы не начать покорение мобильных платформ именно с него? Тем более что в этой области нередко возникают одноразовые проекты (без дальнейшей поддержки), и ради них всерьез изучать Objective-C попросту нет смысла.
Пример из практики: однажды мне выпало делать проект разработки приложения для корпоративного интернет-магазина. Задача по факту одноразовая: «упрощаем покупателям жизнь и отправляем приложение в свободное плавание». В таких случаях хочется быстрее решить задачу с минимальными затратами. Как «серебряная пуля» на этом поприще давно зарекомендовал себя PhoneGap. Он генерирует каркас будущего приложения и приравнивает создание мобильного приложения к созданию типового сайта. Вот и получается, что при наличии средних знаний HTML/CSS/JS вполне себе реально собрать приличное приложение.
Платформа PhoneGap предоставляет все необходимое для создания приложений, но дизайнерские тонкости оставляет на наше усмотрение. Грубо говоря, вот так просто взять и собрать приложение с приближенным к нативному исполнению интерфейсу не получится. Без привлечения дополнительных технологий тут не обойтись.
«Бутстрап» для мобильных платформ
Современные веб-разработчики наслышаны о мощи и неуклюжести фреймворка Bootstrap. Он позволяет творить чудеса и за считаные минуты создавать прототипы современных веб-приложений. Framework7 — это своего рода Bootstrap, но нацеленный на мобильные платформы. Как и подобает хорошему фреймворку, в F7 собраны всевозможные виджеты и компоненты, позволяющие создать приложение, максимально похожее на нативное.
Изначально F7 специализировался сугубо на платформе iOS. Стандартная тема оформления была ориентирована на iOS 7 и по сравнению с конкурентами выделялась производительностью интерфейса. Совсем недавно разработчики анонсировали поддержку Material-дизайна от Google, тем самым добавив в список поддерживаемых платформ Android.
Разработчики проекта постарались навести под капотом порядок и оставить только действительно необходимые вещи. В итоге привычных нам штук вроде встроенной библиотеки jQuery в F7 не найти. Вместо нее доступна легковесная Dom7, обладающая большинством необходимых возможностей.
Итак, резюмируем. F7 — это JS/CSS-фреймворк со всеми необходимыми UI-элементами, выполненными в нативном для мобильной платформы стиле. Скажу честно, подобные фреймворки уже светились на GitHib, но F7 выгодно отличается высокой производительностью и реализацией многих нативных UX-фишек. Например, такие привычные для пользователей iOS, как Pull to refresh («потяни для обновления»), Swipe, back bar, и многие другие доступны из коробки и не требуют дополнительного программирования.
На этом сильные стороны F7 не заканчиваются. Не буду вдаваться в подробности, а лишь немного поделюсь впечатлениями.
Начну с наиболее важного для меня атрибута качественного проекта — документации. Ожидать появления книг по таким скороспелым вещам бессмысленно — пока их будут писать, фреймворк наверняка обновится и текст потеряет актуальность. Вся надежда в таких проектах на документацию. Чем она подробней, тем лучше. В F7 с этим полный порядок. Как мне показалось, документирована большая часть проекта, а там, где не хватило текста, разработчики привели примеры кода.
О потрясающей производительности я уже упомянул. Достигается она в первую очередь благодаря жесткой диете и использованию актуальных современных возможностей JavaScript. Что касается замены jQuery на Dom7, то переживать не стоит. Основные методы в ней реализованы точно так же. Название однотипных методов, порядок параметров полностью сохранены. Следовательно, привыкнуть будет нетрудно.
На официальном сайте проекта представлены различные графики, подтверждающие производительность F7, но в таких вопросах я больше доверяю своему восприятию. После разработки первого реального проекта время отклика интерфейса я проверил самостоятельно. На последних моделях iPhone (5, 5S, 6) оно выше всяческих похвал. Все работает вполне естественно и привычно.
Из других приятных плюшек для себя я отметил применение языка Less для описания стилей. При разработке веб-приложений с этой технологией приходится сталкиваться постоянно, поэтому чертовски приятно, что полученные навыки можно смело использовать на мобильных платформах.
Пробуем на практике
Технологии регулярно сменяют друг друга, но одно остается неизменным: лучший способ познакомиться с ними — это практика. Для демонстрации работы F7 я решил написать полезное приложение, которое обязательно пригодится всем нашим читателям, — читалку новостей с сайта любимого журнала. Поскольку в текущей реализации у нашего сайта нет полноценного API для получения материалов, мы воспользуемся старым добрым протоколом RSS.
Создать читалку на стеке HTML/CSS/JS/F7/PhoneGap не слишком сложно, поскольку для работы с RSS уже создан достаточно функциональный плагин. В остальном работа сведется к написанию нескольких десятков строк тухлого HTML. Это довольно скучно, поэтому я взял на себя смелость добавить немного рок-н-ролла. Пример останется тем же, но писать мы его будем в MVC-стиле. В итоге мы получим своеобразный микрофреймворк с прицелом на будущее. Говоря другими словами, мы создадим универсальный каркас для последующей разработки хорошо расширяемых приложений.
Резюмируя перечисленные выше мысли, получаем примерно такой план действий.
Первым делом готовим основу для проекта с помощью актуального стека технологий, затем создаем средствами F7 интерфейс приложения и на заключительном шаге заливаем эту ядовитую смесь в PhoneGap. Приложение будем ориентировать на iOS, нюансы разработки под Android оставлю тебе в качестве домашнего задания.
Есть несколько способов организации паттерна MVC в JavaScript, но мы воспользуемся вариантом от Филиппа Шурпика. Он достаточно простой, и я его уже успел опробовать (с некоторыми доработками) в своих реальных проектах. Что касается дополнительных компонентов/библиотек, то, помимо F7, нам потребуются:
- RequireJS — одно из лучших решений для организации AMD (Asynchronous module definition) подхода;
- handlebars — один из самых быстрых шаблонизаторов для JavaScript;
- hbs — простой handlebars для RequireJS;
- text.js — еще один плагин для RequireJS, позволяющий подгружать текстовые ресурсы.
Все возможности перечисленных компонентов мы рассмотреть не сможем, да и в рамках нашего примера можно было обойтись меньшей кровью. Напомню, наша цель — не только познакомиться с фреймворком F7, но и получить каркас приложения для дальнейших испытаний.
Структура приложения
Функциональность нашего приложения предельно проста: подтягиваем обновленную RSS-ленту и предоставляем пользователю возможность комфортного чтения. Загрузка полного текста новости напряжет пользователя, поэтому будем придерживаться минимализма — сначала отображаем заголовки, а полную версию текста пользователь сможет прочитать после тапа по нему.
Отлично, организационные моменты решили, осталось спроектировать структуру будущего приложения. Вариантов, как всегда, несколько, но, поскольку законченное веб-приложение мы будем скармливать PhoneGap, наиболее оптимальным вариантом будет:
- css — для хранения собственных стилей оформления. Все то, что мы переопределяем или дорабатываем, помещаем в эту директорию;
- img — изображения;
- js — весь клиентский JavaScript. Здесь размещаем только собственные сценарии, а не библиотеки. В корне директории располагаем сценарии общего назначения и модели. Контроллеры и представления определяем в одноименных поддиректориях. Смотрим пример, для контроллера about будем создавать папку js/about;
- libs — библиотеки и всевозможные дополнительные плюшки. Например, решил ты подключить великолепный Font awesome — кидаешь его сюда.
В корневой директории проекта разместятся всего лишь два файлика — app.js
и index.html
. Первый файл будет стартовой точкой приложения. В нем выполним конфигурирование вспомогательных библиотек и проинициализируем F7.
Готовим каркас
Загружаем перечисленные библиотеки (bower, git) и распихиваем их по соответствующим директориям. Затем в корне проекта создаем файлик app.js и выполняем конфигурирование дополнительных компонентов.
Конфигурация для RequireJS описана в первом листинге (эх, лет десять назад пытался Никита Кислицын запретить Игорю использовать скучное слово «листинг», но, похоже, победил Антонов :). — Прим. ред.). Здесь мы подключаем дополнительные библиотеки. Поскольку handlebars не оформлен в AMD-стиле, подключение выполняется через shim. В принципе, плагин для чтения RSS мы могли бы подключить точно таким же способом, но поскольку наше приложение и так не может без него существовать, то его инициализацию будем делать по старинке, через стартовый файл index.html
:
require.config({
urlArgs: "fake=" + (new Date()).getTime(),
paths: {
handlebars: "libs/handlebars",
text: "libs/text",
hbs: "libs/hbs"
},
shim: {
handlebars: {
exports: "Handlebars"
}
}
});
Разработка демонстрационного приложения подразумевает постоянное внесение всевозможных исправлений, поэтому сразу ограничим аппетиты кеширования RequireJS. Отдельного параметра для этого не существует, но получить необходимый эффект можно с помощью свойства urlArgs.
Принцип прост: к каждому URL добавляем указанный в свойстве аргумент с определенным значением. Чтобы избавиться от кеширования, для аргумента необходимо подбирать уникальное значение. Каждый решает эту задачу по-своему, но для уникальности получаю текущее время:
urlArgs: "fake=" + (new Date()).getTime()
Ниже приведен код модуля app. В нем мы выполняем инициализацию системы маршрутизации (router.js) и самого фреймворка. Описываем все в виде AMD-модуля. Для объявления модуля применяется метод define(). В первом параметре передаем название модуля, во втором перечисляем зависимости, а третьим описываем тело модуля. Подробности смотри в документации к RequireJS.
define('app', ['js/router'], function(Router){
Router.init();
var f7 = new Framework7();
var mainView = f7.addView('.view-main', {
dynamicNavbar : true
});
return {
f7: f7,
mainView: mainView,
router: Router,
};
});
Тело модуля начинается с инициализации модуля маршрутизации (см. файл js/router.js
). Роутер будет разруливать маршруты и запускать соответствующий метод контроллера. Сам роутинг реализуется достаточно просто (см. листинг 3): на входе получаем имя контроллера и пытаемся вызвать его заранее определенный метод init(). Путь к контроллеру (файлу) определить несложно — на этапе обсуждения структуры приложения мы договорились сохранять их в папке js/имяКонтроллера/имяController.js
.
Закончив с роутингом, приступаем к инициализации Framework7. В самом простом случае никаких параметров конструктору передавать не требуется, но если хочется сразу все максимально твикнуть под себя, то параметром можно передать объект с настройками. Всевозможных полей у объекта много, поэтому сразу рекомендую обратиться к документации и внимательно изучить предназначение каждого из них. Наибольшего внимания заслуживают fastClicks, cache, cacheDuration, material.
function load(controllerName, query) {
require(['js/' + controllerName + '/'+ controllerName + 'Controller'], function(controller) {
controller.init(query);
});
}
Дальше инициализируется область представления. В контексте F7 под областью представления (View) подразумевается отдельная визуальная часть приложения. Каждая область представления характеризуется собственными настройками, навигационной панелью и рядом других элементов.
Инициализировать нужно только те области представления, которым требуется навигация. В нашем случае это .main-view. Сама инициализация сводится к вызову метода addView(). Он просит от нас два параметра: селектор области представления и объект с параметрами.
Покоряем RSS
У нас все готово для разработки интерфейса приложения. Основную его часть опишем в файле index.html, расположенном в корне проекта (обязательно его создай). Текст разметки (HTML-код) имеет изрядный размер, поэтому копировать сюда его не стану, а направлю тебя в раздел документации Basic App Layout. Смело бери оттуда весь исходник HTML, копируй подготовленный файл и приготовься внести несколько правок. Начнем с секции подключения сценариев. Приводим к следующему виду:
<script type="text/javascript" src="libs/framework7.js"></script>
<script type="text/javascript" src="libs/framework7.feeds.js"></script>
<script data-main="app" src="libs/require.js"></script>
Нам обязательно требуется подключить сам фреймворк и плагин Feeds (для работы с RSS). В самом конце инклудим библиотеку RequireJS. Далее немного скроллим текст и находим блок
<div class="page-content"></div>
В эту область будем выводить содержимое определенных представлений. У нас планируется одно-единственное представление, поэтому не будем заморачиваться и легким движением руки добавим поддержку функциональности «потяни и обнови». Для этого прописываем в блок дополнительный класс pull-to-refresh-content и получаем примерно следующее:
<div class="page-content pull-to-refresh-content"></div>
Очередным шагом подключим дополнительные стили в шапку (framework7.feeds.min.css
) и приступим к созданию контроллера. Я не стал акцентировать внимание на изменении заголовка приложения и добавлении вспомогательного текста — всю эту косметику ты сможешь сделать самостоятельно.
Приемы в стиле MVC
Нашему проекту потребуется один контроллер, назовем его index и подготовим отдельную директорию в папке /js
. Сразу в ней создавай несколько файликов:
indexController.js
— непосредственно контроллер;indexView.js
,index.hbs
— представление и шаблон.
Модель нам не потребуется, но для примера в корне директории js создан пустой файл-заглушка feedModel.js. При необходимости описываем в нем модель и получаем к ней доступ из контроллера.
Посмотрим на содержимое контроллера (листинг 4). Первое, что бросается в глаза, — формат объявления. Наш контроллер — это не что иное, как обычный модуль с одним методом init, получающий порцию зависимостей.
После запроса индексной страницы будет вызван метод init контроллера. Дальше все зависит от задачи. Можем получить какие-нибудь данные и передать их в представление, можем что-то обработать. В нашем примере все ограничивается формированием представления. Для этого вызываем метод render(). Передавать данные из контроллера в представление можно через его единственный параметр. Нам передавать ничего не требуется, поэтому просто передадим объект-заглушку.
После того как представление будет сформировано, DOM пополнится новыми узлами и мы можем сделать с ними что-нибудь полезное. Например, выполнить инициализацию плагина Feeds. Для этого определим селектор для вывода и объект с настройками. Из настроек необходим путь к RSS-ленте и способ отображения (на странице, в окне):
define(["app","js/index/indexView", "js/feedModel"], function(app, IndexView, Index) {
function init(query) {
IndexView.render({
model: { message: 'test'}
});
var myFeed = app.f7.feeds('.feed', {
url: 'http://localhost/feed.xml',
openIn: 'page'
});
}
return {
init : init
};
});
Код представления приведен по организации похож на контроллер. Тот же модуль и одна-единственная функция. Обрати внимание на использование переменной $. Это не библиотека jQuery, а Dom7. Многие их методы идентичны, но в Dom7 есть далеко не все, поэтому будь внимательней.
define(['js/feedModel', 'hbs!js/index/index'], function(Index, viewTemplate) {
var $ = Dom7;
function render(params) {
$('.page-content').html(viewTemplate({ model: params.model }));
}
return {
render: render
}
});
Данные для вывода мы будем получать из RSS-ленты, поэтому шаблон представления содержит стандартный HTML. Стоит обратить внимание на добавление функциональности Pull to refresh (потяни и обнови). Ранее мы добавили соответствующий класс в index.html, а в представлении лишь завершили начатое. Код для запроса обновленной ленты писать не требуется, плагин RSS Feed из коробки поддерживает Pull to refresh.
<div class="pull-to-refresh-layer">
<div class="preloader"></div>
<div class="pull-to-refresh-arrow"></div>
</div>
<div class="content-block-title">Список новостей с xakep.ru</div>
<div class="list-block feed"></div>
На этом разработка приложения завершена. Можешь протестировать его с помощью локального веб-сервера (например, входящего в состав gulp).
Подготавливаем PhoneGap
Веб-приложение готово, и теперь остается только собрать его с помощью платформы PhoneGap. Для установки PhoneGap нам потребуется установленный в системе Node.js. Если ты не отстаешь от современных трендов, то наверняка он уже есть в твоей системе. Если нет, то беги на официальный сайт и следуй инструкциям.
Хорошо, будем считать, что с установкой Node.js ты справился. Теперь установим PhoneGap:
$ sudo nmp install -g phonegap
Отлично, но одного PhoneGap недостаточно. Без инструментов Cordova command-line тоже не обойтись:
$ sudo npm install -g cordova
Из других вспомогательных инструментов нам понадобится тулза для автоматизации процесса сборки Ant. Установить Ant можно несколькими способами. Проще всего это сделать с помощью менеджера пакетов. Для OS X их несколько, но у меня прижился Homebrew. Работает стабильно и содержит большое количество пакетов. Установить Homebrew достаточно просто. Набираем в терминале команду
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Сразу после завершения установки беремся за Ant:
$ brew update
$ brew install ant
Следующим шагом будет установка Xcode. Набираемся терпения и устанавливаем актуальную версию из App Store. После установки обязательно запускаем его и принимаем лицензионное соглашение. Если этого не сделать, то PhoneGap не сможет собрать проект.
Собираем мобильное приложение
Не отходя далеко от консоли, создадим новый проект мобильного приложения. Вводим в консоли:
$ phonegap create xakepRssReader
$ cd xakepRssReader
Отлично, базовая заготовка закончена — можно приступать к переносу нашего веб-приложения. Перейди в директорию www
и удали из нее все содержимое. Затем скопируй в нее все файлы и папки созданного нами приложения. В результате весь наш проект должен разместиться в папке www
. Возвращайся в консоль и приступай к сборке. Сначала определим мобильную платформу для сборки (в нашем случае iOS), а затем запустим деплой проекта:
$ cordova platform add ios
$ cordova build ios
Если нет ошибок, в консоль будет выведена надпись Build Succeeded. Остается только открыть из директории platforms/js
файл xakepRssReader.xcodeprof в Xcode и запустить процесс сборки (нажимаем кнопку Play). Если все пройдет успешно (а должно быть именно так), то через несколько секунд запустится окно эмулятора (в моем случае iPhone 6).
Сборка завершена
Собрать мобильное приложение при помощи знакомого каждому веб-программисту стека технологий на практике оказалось не так уж и сложно. Если посмотреть в App Store, то большинство корпоративных приложений суть трансляция определенного контента/сервиса с официального сайта компании. Можно ли создавать подобные вещи, не прибегая к нативным технологиям? Определенно, да.
Разработчики видят перспективы веба в мобильной среде и стараются перенести в нее как можно больше трендовых технологий. Фреймворки вроде рассмотренного в сегодняшней статье лишний раз подтверждают: невозможного не существует. Производительность мобильных устройств продолжает расти, и это только подстегивает к переносу привычных технологий в новую среду.
Безусловно, не стоит питать излишних иллюзий и отказываться от изучения нативных технологий (Objective-C, Swift). Если ты серьезно настроен на разработку под мобильные платформы, то обойтись одними веб-технологиями не удастся. Во всяком случае, сейчас. Но если твоя цель — попрактиковаться и неплохо подзаработать на типовых проектах, то веб-технологии смогут помочь.
На этом у меня все. Удачной мобильной разработки!
Плюсы PhoneGap
- Процесс создания максимально похож на разработку веб-приложения;
- единый стек технологий (HTML/CSS/JavaScript);
- низкий порог вхождения, быстрые результаты;
- покрытие всех популярных мобильных платформ (iOS, Android, Windows Phone);
- низкая стоимость разработки приложения;
- более дешевое сопровождение;
- возможность использования JS-наработок.
Минусы PhoneGap
- Более низкая производительность по сравнению с нативными приложениями;
- ограничения платформы;
- бедные возможности отладки;
- UI отличается от нативного (решается различными фреймворками).
WWW
Потестируй сам
- DevExtreme — HTML5-фреймворк для разработки под различные платформы. Сборка приложения выполняется с помощью PhoneGap. Содержит набор виджетов для всех поддерживаемых мобильных ОС. Бесплатен для некоммерческого использования.
- Ionic framework — еще одна обертка над PhoneGap, упрощающая процесс создания гибридных мобильных приложений. Из коробки предоставляет шаблоны (заготовки) для мобильных приложений. Под капотом интегрирован AngularJS, SASS. Фреймворк чрезвычайно популярен, и почти 18 тысяч звезд на GitHub — лишнее тому подтверждение.
- Ionic + Material — трендовый Material-дизайн для Ionic framework.
- Ratchet — одна из интересных альтернатив Framework7.