Разработка мобильных Javascript MVC приложений с Framework7, RequireJS и Handlebars

Недавно передо мной стала задача разработки IPhone и Android приложения. Опыта разработки под IOS у меня ранее не было, да и хотелось написать один раз и запускать на обеих платформах. Соответственно был выбран был выбран Javascript и PhoneGap.

И если с языком я определился относительно быстро, то далее было много вопросов.
Хотелось сделать, что бы приложение максимально повторяло интерфейс IOS7 и было похоже на native по скорости работы. При этом с одной стороны не было желания использовать «монстров», на подобии dojo или jquery mobile. c другой стороны хотелось получить удобную модульную MVC структуру приложения.

В итоге в финал моего личного сравнения вышли:
Ionic framework: http://ionicframework.com/
Framework7: http://www.idangero.us/framework7/

У Ионика сначала мне понравилась документация, простые примеры и знакомая по AngularJs структура кода. Но после первых попыток создать приложение наступило разочарование. Запущенное простое приложение на Iphone5 тормозило. При нажатии на кнопки или навигации была визуально заметна задержка между нажатием и срабатыванием. На подобии 300мс задержки при клике. Но по заявлениям создателей их фреймворк содержит собственную реализацию библиотеки fastclick… Странно. Так же даже в простом приложении временами были заметны подтормаживания в анимации. В итоге после пары дней чтения документации и тестовых примеров я понял, что надо искать что-то еще.

Дальше я вернулся к Framework7. Запустил тестовые приложения, глянул компоненты в kitchen sink и первоначально испытал wow эффект. На IPhone все работает быстро, красиво и очень похоже на native. При этом столкнулся с двумя достаточно большими минусами:
  • На тот момент практически отсутствовала документация. Сейчас она уже есть, достаточно подробная (http://www.idangero.us/framework7/docs/).
  • Во всех примерах код был в одном файле-простыне в jquery-like формате. При этом отсутствовала модульность, подгрузка шаблонов из отдельных файлов и т.п.

В общем я подтянул свои теоретические знания, просмотрел различные статьи и примеры и смог решить для себя задачу по совмещению Framework7 и модульного MVC подхода для создания мобильных приложений. Для реализации асинхронной загрузки модулей использовал RequireJs, для шаблонов – Handlebars.

Соответственно создал парочку учебных примеров и сейчас хочу поделиться ими с сообществом. Надеюсь они будут полезны, как начинающим разработчикам, так и более опытным, которые пока еще не знают про данный фреймворк.

Начинаем


Для работы нам потребуются следующие библиотеки:
  • Framework7
  • Handlebars – необходим для шаблонов
  • RequireJS – асинхронная загрузка модулей
  • Дополнительные плагины к RequireJs для загрузки шаблонов:
  • А также хочу вас познакомить с прекрасной библиотекой иконок – ionicons http://ionicons.com/


Структура проекта



Создадим следующую структуру файлов проекта (файлы index.html и app.js пока оставим пустыми)
Что бы упростить себе жизнь – можно скачать архив со структурой по этой ссылкe:
Dropbox
(В данном архиве уже заполнены первые версии файлов index.html и app.js)

Также сразу даю ссылку на исходники на Github — там лежит последняя версия вместе с пошаговой историей правок — создания данного тестового приложения:
https://github.com/philipshurpik/Framework7-MVC-base

Создадим самый простой index.html файл, в котором подключим все необходимые библиотеки:

<!DOCTYPE html>
<html class="with-statusbar-overlay">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
	<meta name="apple-mobile-web-app-capable" content="yes">
	<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
	<title>F7 Contacts MVC</title>
	<link rel="stylesheet" href="lib/css/framework7.css">
	<link rel="stylesheet" href="lib/css/ionicons.css">
	<link rel="stylesheet" href="css/app.css">
</head>
<body>
<div class="statusbar-overlay"></div>
<div class="views">
	<div class="view view-main navbar-fixed">
		<div class="navbar">
			<div class="navbar-inner">
				<div class="left"></div>
				<div class="center" style="left:22px">Contacts</div>
				<div class="right">
					<a href="contact.html" class="link icon-only"><i class="icon icon-plus">+</i></a>
				</div>
			</div>
		</div>
		<div class="pages">
			<div data-page="list" class="page">
				<div class="page-content">
					<div class="list-block contacts-list">
						<ul>
							<a href="contact.html" class="item-link item-content">
								<div class="item-media"><i class="icon ion-ios7-person"></i></div>
								<div class="item-inner">
									<div class="item-title">Andrey Smirnov</div>
								</div>
							</a>
							<a href="contact.html?id={{id}}" class="item-link item-content">
								<div class="item-media"><i class="icon ion-ios7-person"></i></div>
								<div class="item-inner">
									<div class="item-title">Olga Kot</div>
								</div>
							</a>
						</ul>
					</div>
				</div>
			</div>
		</div>
	</div>
</div>
</body>
</html>
<script type="text/javascript" src="lib/framework7.js"></script>
<script type="text/javascript" src="app.js"></script>


Также в файл app.js поместим инициализацию приложения:
var f7 = new Framework7({
	modalTitle: 'F7-MVC-Base',
	animateNavBackIcon: true
});
var mainView = f7.addView('.view-main', {
	dynamicNavbar: true
});


Запустим и получим следующую картинку:

Вот. У нас есть первая страничка и на ней даже что-то больше, чем hello-world.

Да, если кто не знает. В Devtools Chrome рядом с консолью есть вкладка Emulation, на которой можно выбрать нужный девайс и посмотреть, как примерно приложение будет выглядеть на экране этого устройства.



Подключаем RequireJs и Handlebars, подгружаем контакты


Теперь нам необходимо динамически подгружать контакты (например из localstorage) и отображать их в списке.
Для этого изменим наши файлы:

1. index.html
Заменим прямое подключение нашего app.js файла на подключение Require.Js
<script data-main="app" src="lib/require.js"></script>
Атрибут data-main указывает на точку входа в приложение (это наш файл app.js)&
Также можно удалить то, что находится внутри тегов ul – внутренности списка будут генерироваться с помощью шаблона.

2. app.js
Переделаем наш файл в RequireJs модуль:
define('app', ['js/list/listController'], function(listController) {
	var f7 = new Framework7({
		modalTitle: 'F7-MVC-Base',
		animateNavBackIcon: true
	});
	var mainView = f7.addView('.view-main', {
		dynamicNavbar: true
	});
	listController.init();
	return {
		f7: f7,
		mainView: mainView
	};
});

Все тоже самое, только обернули в модуль + добавили загрузку нашего первого контроллера, которого пока еще нету.

Главная страница: контроллер, представление, template элемента


Теперь нам необходимо создать контроллер для главной страницы, ее представление, а также handlebars template.
Предлагаю назвать и разместить файлы следующим образом:

Да, подобная группировка – по функциональности – как мне кажется намного удобнее в проектах, чем размещение представлений, моделей, контроллеров в разных директориях.

Создадим простой контроллер для списка. И в нем сразу же инициализируем наш localstorage несколькими объектами контактов:

Файл: js/list/listController.js
define(["js/list/listView"], function(ListView) {

	function init() {
		var contacts = loadContacts();
		ListView.render({ model: contacts });
	}

	function loadContacts() {
		var f7Base = localStorage.getItem("f7Base");
		var contacts = f7Base ? JSON.parse(f7Base) : tempInitializeStorage();
		return contacts;
	}

	function tempInitializeStorage() {
		var contacts = [
			{id: "1", firstName: "Alex", lastName: "Black", phone: "+380501234567" },
			{id: "2", firstName: "Kate", lastName: "White", phone: "+380507654321" }
		];
		localStorage.setItem("f7Base", JSON.stringify(contacts));
		return JSON.parse(localStorage.getItem("f7Base"));
	}

	return {
		init: init
	};
});


Так же теперь нам необходимо добавить представление, которое будет отвечать за рендеринг наших данных (которые мы передаем при его инициализации) с помощью темплейта.
Файл: js/list/listView.js
define(['hbs!js/list/contact-list-item'], function(template) {
	var $ = Framework7.$;

	function render(params) {
		$('.contacts-list ul').html(template(params.model));
	}

	return {
		render: render
	};
});


А также код нашего простого темплейта:
Файл: js/list/contact-list-item.hbs
{{#.}}
	<a href="contact.html?id={{id}}" class="item-link item-content">
		<div class="item-media"><i class="icon ion-ios7-person"></i></div>
		<div class="item-inner">
			<div class="item-title">{{firstName}} {{lastName}}</div>
		</div>
	</a>
{{/.}}


Запускаем — и получаем — все тоже самое, но модульное и гораздо более расширяемое.

Теперь нам необходимо добавить страницу просмотра и редактирования контакта.

Навигация между страницами в Framework7


Каждая страница размещена в отдельном html файле.
Страница содержится внутри div c class=”page”
<div class="page" data-page="list">

Аттрибут data-page определяет уникальное название страницы которое будет нам необходимо в дальнейшем для роутинга.
Все визуальные элементы страницы необходимо размещать внутри:
<div class="page-content"> который является дочерним для <div class="page">

Навигация между страницами осуществляется или при нажатии на html ссылку:
<a href="about.html">Go to About page</a>
Bли из js кода:
app.mainView.loadPage('about.html');

Навигация назад (вместе с анимацией) осуществляется аналогично:
Или добавлением класса back в ссылку:
<a href="index.html" class="back"> Go back to home page </a>
Или из js кода:
app.mainView.goBack();

При переходе между страницами Framework7 генерирует события, на которые можно подписаться:
PageBeforeInit, PageInit, PageBeforeAnimation, PageAfterAnimation, PageBeforeRemove

Полная информация о страницах и событиях тут:
http://www.idangero.us/framework7/docs/pages.html
http://www.idangero.us/framework7/docs/ linking-pages.html

Создаем router.js


Воспользуемся событием, которое возникает после вставки новой страницы в DOM – PageBeforeInit.
Создадим простой роутер (файл router.js) и положим его в папку js, в котором подпишемся на возникновение события pageBeforeInit:

define(function() {
	var $ = Framework7.$;

	function init() {
		$(document).on('pageBeforeInit', function (e) {
			var page = e.detail.page;
			load(page.name, page.query);
		});
    }

	function load(controllerName, query) {
		require(['js/' + controllerName + '/'+ controllerName + 'Controller'], function(controller) {
			controller.init(query);
		});
	}

	return {
        init: init,
		load: load
    };
});

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

Также переделаем модуль app.js, добавим в него инициализацию роутера и уберем подключение и инициализацию контроллера:
define('app', ['js/router'], function(Router) {
	Router.init();
	var f7 = new Framework7({
		modalTitle: 'F7-MVC-Base',
		animateNavBackIcon: true
	});
	var mainView = f7.addView('.view-main', {
		dynamicNavbar: true
	});
	return {
		f7: f7,
		mainView: mainView,
		router: router
	};
});

Теперь при первой загрузке приложения, после вставки главной страницы в DOM сработает обработчик события pageBeforeInit.
При этом его свойство e.detail.page.name будет равняться list, то есть тому, что было задано тут в свойстве data-page: Соответственно будет запущен соответствующий контроллер.

Страница редактирования контакта


Далее необходимо создать страницу добавления и редактирования контакта.
Добавим в корень проекта html файл contact.html (если вы скачивали структуру файлов из архива, то он там уже должен быть)
Соответствующие ссылки на contact.html уже были добавлены ранее в navbar главной страницы и в темплейт элементов списка контактов.
<div class="navbar">
	<div class="navbar-inner">
		<div class="left sliding">
			<a href="#" class="back link">
				<i class="icon icon-back-white"></i>
				<span>Back</span>
			</a>
		</div>
		<div class="center contacts-header"></div>
		<div class="right contact-save-link">
			<a href="#" class="link">
				<span>Save</span>
			</a>
		</div>
	</div>
</div>
<div class="pages">
	<div data-page="contact" class="page contact-page">

	</div>
</div>


Теперь при нажатии на элемент списка или на кнопку добавить – роутер пробует загрузить файл js/contact/contactController.

Соотвественно нам необходимо создать его, представление страницы, а так же шаблон содержимого страницы. Вот так:


Содержимое файла contactController.js:
define(["app","js/contact/contactView"], function(app, ContactView) {

	var state = {isNew: false};
	var contact = null;

	function init(query){
		if (query && query.id) {
			var contacts = JSON.parse(localStorage.getItem("f7Base"));
			for (var i = 0; i< contacts.length; i++) {
				if (contacts[i].id === query.id) {
					contact = contacts[i];
					state.isNew = false;
					break;
				}
			}
		}
		else {
			contact = { id: Math.floor((Math.random() * 100000) + 5).toString()};
			state.isNew = true;
		}
		ContactView.render({
			model: contact,
			state: state
		});
	}

	return {
		init: init
	};
});

Если страница в режиме редактирования (в query содержится значение id контакта, то получаем его из localStorage.
Если нет, то создаем новый. Пока что для простоты мы не используем модели, поэтому наш контакт – это просто объект.

Также страница представления contactView.js:
define(['hbs!js/contact/contact'], function(viewTemplate) {
	var $ = Framework7.$;

	function render(params) {
		$('.contact-page').html(viewTemplate({ model: params.model }));
		$('.contacts-header').text(params.state.isNew ? "New contact" : "Contact");
	}

	return {
		render: render
	}
});

И шаблон contact.hbs:
<div class="page-content">
	<form id="contactEdit" class="list-block">
		<ul>
			<input name="id" type="hidden" value="{{model.id}}">
			<li>
				<div class="item-content">
					<div class="item-media"><i class="icon ion-ios7-football-outline"></i></div>
					<div class="item-inner">
						<div class="item-input">
							<input name="firstName" type="text" placeholder="First name" value="{{model.firstName}}">
						</div>
					</div>
				</div>
			</li>
			<li>
				<div class="item-content">
					<div class="item-media"><i class="icon ion-ios7-football-outline"></i></div>
					<div class="item-inner">
						<div class="item-input">
							<input name="lastName" type="text" placeholder="Last name" value="{{model.lastName}}">
						</div>
					</div>
				</div>
			</li>
			<li>
				<div class="item-content">
					<div class="item-media"><i class="icon ion-ios7-telephone-outline"></i></div>
					<div class="item-inner">
						<div class="item-input">
							<input name="phone" type="tel" placeholder="Phone" value="{{model.phone}}">
						</div>
					</div>
				</div>
			</li>
		</ul>
	</form>
</div>

Ну что же. Теперь мы можем открыть нашу страницу добавления или редактирования контакта:


Осталось добавить возможность контакты сохранять и удалять.
Начнем с сохранения.

Сохранение контактов


Для начала добавим обработчик кнопки сохранить.
Конечно можно сделать это сразу напрямую в контроллере вот так:
$(‘.contact-save-link’).on(‘click’, function() {
 // some code here
});

Но так делать не хорошо, и лучше отделять работу с DOM и работу с данными и моделями.
Поэтому разделим подписку на обработку события и саму обработку.
В контроллере сделаем массив bindings:
var bindings = [{
		element: '.contact-save-link',
		event: 'click',
		handler: saveContact
	}];

Передадим этот массив в качестве одного из свойств объекта params в представление.

И добавим функцию-обработчик:
function saveContact() {
// some code here
}

А в представлении добавим подписку на события по данному конфигу – функцию bindEvents:
	function bindEvents(bindings) {
		for (var i in bindings) {
			$(bindings[i].element).on(bindings[i].event, bindings[i].handler);
		}
	}

И ее вызов из функции render:
bindEvents(params.bindings);

Теперь необходимо получить значение данных введенные в форму:
Делаем это в функции saveContact:

function saveContact() {
		var contacts = JSON.parse(localStorage.getItem("f7Base"))
		var newContact = app.f7.formToJSON('#contactEdit');
		if (state.isNew) {
			contacts.push(newContact)
		}
		else {
			for (var i = 0; i< contacts.length; i++) {
				if (contacts[i].id === newContact.id) {
					contacts[i] = newContact;
					break;
				}
			}
		}
		localStorage.setItem("f7Base", JSON.stringify(contacts));
		app.router.load('list');
		app.mainView.goBack();
	}

Так же полученные данные сохраняем сразу в localStorage.
Последние две строчки отвечают за возврат на предыдущую страницу (список), а также перезагрузку данных в listController.

У нас теперь все работает!

Создание модели:


Но так оперировать всеми данными в контроллере не очень хорошо. К тому же иногда необходимо добавить специальные функции – например по валидации данных.

Поэтому сделаем модель в файле js/contactModel.js.
За одно добавим в нее функцию валидации, а также установки значений из другого объекта.

define(['app'],function(app) {

	function Contact(values) {
		values = values || {};
		this.id = values['id'] || Math.floor((Math.random() * 100000) + 5).toString();

		this.firstName = values['firstName'] || '';
		this.lastName = values['lastName'] || '';
		this.phone = values['phone'] || '';
	}

	Contact.prototype.setValues = function(formInput) {
		for(var field in formInput){
			if (this[field] !== undefined) {
				this[field] = formInput[field];
			}
		}
	};

	Contact.prototype.validate = function() {
		var result = true;
		if (!this.firstName && !this.lastName) {
			result = false;
		}
		return result;
	};

	return Contact;
}); 

Заметьте, функции добавляются не в сам объект, а в его прототип. Соответственно при передаче или сохранении объекта в JSON передаются только его свойства, без функций.

Теперь подключим модель в contactController:
Добавим в список зависимостей:
define(["app","js/contact/contactView", "js/contactModel"], function(app, ContactView, Contact)

Изменим в функции init соответственно присвоение и создание контакта:
contact = new Contact(contacts[i]);

и
contact = new Contact();

И модифицируем функцию save, добавив в нее запуск валидации модели:
function saveContact() {
		var formInput = app.f7.formToJSON('#contactEdit');
		contact.setValues(formInput);
		if (!contact.validate()) {
			app.f7.alert("First name and last name are empty");
			return;
		}
		var contacts = JSON.parse(localStorage.getItem("f7Base"));
		if (state.isNew) {
			contacts.push(contact);
		}
		else {
			for (var i = 0; i< contacts.length; i++) {
				if (contacts[i].id === contact.id) {
					contacts[i] = contact;
					break;
				}
			}
		}
		localStorage.setItem("f7Base", JSON.stringify(contacts));
		app.mainView.goBack();
		app.router.load('list');
	}

Сохранение готово.

Swipe to delete


Осталось добавить удаление из списка контактов.
Реализуем это с помощью жеста Swipe To Delete в списке.
Модифицируем разметку шаблона элементов:
{{#.}}
	<li id="{{id}}" class="swipeout">
		<a href="contact.html?id={{id}}" class="item-link item-content swipeout-content">
			<div class="item-media"><i class="icon ion-ios7-person"></i></div>
			<div class="item-inner">
				<div class="item-title">{{firstName}} {{lastName}}</div>
			</div>
		</a>
		<div class="swipeout-actions">
			<div class="swipeout-actions-inner">
				<a href="#" class="swipeout-delete">Delete</a>
			</div>
		</div>
	</li>
{{/.}}

Добавим в listController подписку на событие:
var bindings = [{
		element: '.swipeout',
		event: 'deleted',
		handler: itemDeleted
	}];

И дальше сделаем по аналогии с подпиской в контактах – передадим в представление и там подпишемся в функции bindEvents(bindings)

А также добавим обработчик события удаления:
function itemDeleted(e) {
		var id = e.srcElement.id;
		var contacts = JSON.parse(localStorage.getItem("f7Base"));
		for (var i = 0; i < contacts.length; i++) {
			if (contacts[i].id === id) {
				contacts.splice(i, 1);
			}
		}
		localStorage.setItem("f7Base", JSON.stringify(contacts));
	}

Смотрим на результат:


Заключение


У нас вышло готовое очень простое мобильное MVC приложение с использованием Framework7.
А сам Framework7 в связке с Phonegap позволяет создавать красивые native-like приложения в первую очередь для IOS. Что может быть полезно для разработчиков, которые плохо знакомы с ObjectiveC.
При этом мы сразу получаем кросс-платформенное приложение, которое отлично и плавно работает на Android 4.4 (и скорее всего должно так же работать и на следующих версиях).
Для нормальной поддержки недорогих Android устройств на предыдущих версиях Android, достаточно отключить анимацию между страницами, что бы получить тоже достаточно приемлимое быстродействие UI.

Исходники проекта вместе с последовательной историей правок доступны тут:
https://github.com/philipshurpik/Framework7-MVC-base

Так же я сделал расширенный учебный пример приложения контактов, имеющий больше фич и использующий больше возможностей Framework7. В нем добавлены левая выдвигающаяся панель меню, popup редактирования, строка поиска и т.д.
Его исходники вот:
https://github.com/philipshurpik/Framework7-Contacts7-MVC
А вот и скриншоты (с котиками):


Надеюсь эти примеры окажутся вам полезными.
Я сам учился на подобных, поэтому и решил создать данную статью.

Буду рад ответить на вопросы.

П.С. Автора данного фреймворка vladimirkharlampidi на хабре пока нету, но если хабровчан заинтересует эта тема — я думаю он тоже будет рад принять инвайт и присоединиться к обсуждению.

П.П.С. Еще я сделал небольшой research по поводу скорости работы на Android, особенно на старых версиях и залил в репозиторий в app.css хаки по оптимизации css анимаций. Возможно какие-то из них войдут в будущие версии фреймворка. Ну и возможно кому-то будут полезны для их приложений.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 26

    0
    С какими багами мобильника / логики столкнулись при разработке?
      0
      Привет!
      По поводу багов — пару раз возникали сложности с phonegap — он то не реагировал на установленные плагины, то Xсode отказывался компилировать проект.
      Помогло просто пересоздание самого проекта phonegap и копирование в него логики и ресурсов из предыдущего. Хотя там скорее мы сами с плагинами что-то намудрили :)

      Так же — проект в первую очередь разрабатывается под IOS и соответственно как-то мы запустили приложение на старом и медленном Андроиде с версией 4.0.
      И ужаснулись тому на сколько все работает медленно. Потом правда отключили анимации в фреймворке (он позволяет это делать + можно отключать в зависимости от версии Андроид) и все стало работать гораздо лучше.

      Но как таковой оптимизации под Андроид пока еще не делали, поэтому пока ничего детально на эту тему сказать не могу.

      В самом Framework7 пока критичных багов не обнаружил.
      Но если что — автор достаточно быстро отвечает на все вопросы на форуме или по почте, а сам фреймворк имеет достаточно хорошо читаемый и структурированый код — можно разобраться и продебажить самому.
      Несколько раз приходилось дебажить, так как не были понятны какие-то моменты в документации и то «как оно работает», но понимание пришло и вопросы отпали.
        0
        Использую кордову, билд проекта, эмуляция, подключение плагинов делается командами через консоль. Кстати новый проект Cordova iOS в Xode тоже не захотел открывать. Помучился с часик и бросил — пока хватает консоли. Скорей всего тоже прийдется пересоздать через чистый проект, пока обхожусь.
        Насчет Framework7 — оказался отличной вещью, создавать приложения можно в целом, не прибегая к использованию JS: многое прописывается атрибутами в HTML. Собрать сайт в виде приложения с слайдерами фоток, переходами во воложенные страницы с обратной навигацией можно без строчки JS, что открывает хорошие перспективы.
        Автору статьи и фреймверка спасибо!
          0
          Да, я тоже все делают через консоль. Единственное у меня команда cordova run ios не работает корректно.
          Поэтому делаю cordova build ios и запускаю в Xcode, выбрав в качестве target свой телефон.
        0
        Да, еще добавлю.
        Это не на тему багов, но на подобную. В стандартном варианте фреймворка анимации на Андроид временами подтормаживают, особенно на старых версиях и старых телефонах.
        Я сделал небольшую оптимизацию анимаций для андроида и закомитил в репозиторий.
        Возможно кому-то пригодиться:
        github.com/philipshurpik/Framework7-Contacts7-MVC/commit/21bf9338ab1b5ccd11a1a64efe9c92deac58ce13
        +1
        Ух дойдут руки, как допишу статью про мобильную разработку, как сделаю всем хорошо.
        На самом деле мобильная разработка из под веба несёт в себе много подводных камней, о многом не задумываешься при старте. Но с другой стороны, обладает кучей плюсов. Но меня точно радует то, что народ начал обращать на это внимание и активно проталкивать webview в массы.
          0
          Кто-нибудь использовал Ratchet?
            0
            Не использовал но с виду Ratchet и Famework7 очень похожи, только в F7 отсутствует комплект стилей для Android. Судя по документации в F7 больше компонент.
            Подскажите, в чем еще они отличаются?
              0
              Я спросил, потому что сам ещё не использовал, но мне очень интересно.
              Я имел дело с ionic, и столкнулся с описанными проблемами, типо тормозов на девайсе.
              Документация догоняет либу через какое-то время. В issues отвечают, но достаточно медленно. Зачастую приходится читать исходный код фреймворка, и он мне не по нраву. При обновлении фреймворка приходится «переприменять» файлы ionic (ionic start myproject) поверх своего проекта, а затем ревертить свои файлы.

              Эти вещи вынудили меня поискать другое решение, потому что сам подход WebView мне очень симпатичен.

              Т.е. на текущем этапе я попробовал ionic (который представляет собой стили + Angular).
              Следующим я бы хотел попробовать Ratchet (который представляет собой стили + jQuery), в связке с каким-нибудь FRP (Bacon/RxJs).
              Framework7 мне тоже достаточно симпатичен, за исключением двух вопросов:
              1. Заточка под iOS: мне очень не нравится, что все гонятся за реализацией дизайна конкретно iOS в вебе. Нужны какие-то базовые стили для Android (или хотя бы дефолтовые стили, которые будут приемлимо смотреться на любом устройстве, такой стиль есть в Ratchet).
              2. У них собственная реализация jQuery, не то, чтобы это было плохо само по себе, но облегчённые реализации jQuery уже есть: jqlite, Zepto, modern jQuery 2.0.

              Так или иначе, очень интересен Ratchet и любые другие фреймворки этого типа.
                +1
                Пока разрабатываю личное приложение / прототип на framework7 и тестирую под LG Nexus / iPhone 5s. Производительность более чем удовлетворяет, хотя и приложение нельзя назвать супер-замудреннм. Есть официальная работа, где в продакшене больше года есть проект Phonegap / Cordova + Sencha. Приложение довольно требовательно к ресурсам — фоторедактор. Производительность гибридных приложений с iphone 5s стала на приемлемом уровне. Андроид пока вызывает основные вопросы, но работает вполне сносно — в Google Play у нас 4-ка.

                1. По поводу заточки под ios — можно всегда кастомизировать, но имхо UI F7 неплохо воспринимается андроид пользователями, то есть все вполне понятно — навигация вопросов не вызывает. Можно добавить несколько стилей, перекрасит тулбары и кнопки и вообще отойти от стандартного интерфейса, таким образом приложение не будет выглядеть инородно на какой то из платформ — оно будет восприниматься просто другим
                2. По поводу кастомной версии jQuery — для повседневный операции: поиск элементов, перемещение по дереву — все традиционно, у меня вопросов не вызывает. Наличие кастомной реализации меня тоже смутило, но начал использовать и забил. Разработчик наверное решил, что так будет лучше с точки зрения производительности (это мое имхо). Для меня теперь это абсолютно не принципиально — главное чтобы хорошо работало.
                  +1
                  Использовал Framework7, общее впечатление положительное.
                  1. В отличие от ionic, он не делает слишком много за меня и не вымещает все прочие не-angular подходы.
                  2. Вёрстка не сильно абстрагирована и переинженерена, при этом как компоненты, так и анимации на уровне ionic (а то и лучше).
                  3. С кастомным Dom7 (jQuery) проблема возникла как только я по привычке передал ему фрагмент HTML-кода. Эта часть jQuery (кстати, весьма интеллектуальная) явно не входит в минимальный набор, который требовался авторам. Сначала подключил Zepto, потом обнаружил, что он модифицирует прототип на лету (о чём меня предупредил Firefox), посмотрел бенчмарки и благополучно ушёл на jQuery 2.
                  4. Обнаружил, что на мобильном девайсе внезапно начинает рулить AMD-подход. Я являюсь сторонником CommonJS модулей (и browserify). Но т.к. на мобильном девайсе нет особой необходимости собирать JS в один файл, решил использовать AMD, который к тому же даёт неожиданный плюс в ленивости подгрузки (что положительно скажется на скорости старта прилаги).
                  5. Сам Framework7 использует LESS, что также является моим предпочтением.

                  Из минусов (то что я такими посчитал):
                  1. Нет блоковой модели box-sizing: border-box по дефолту. Включить я его по дефолту также не могу, потому что это сломает фреймворк, в итоге можно накидывать sizing только на свои компоненты.
                  2. Почему-то не сделали no-select.
                  3. Не совсем понятна логика понятий views/pages и их разделение.
                  0
                  По поводу вопросов к Framework7:
                  1. Как уже написали — с помощью стилей можно какие-то мелочи кастомизировать. Плюс сам по себе дизайн ios7 является достаточно универсальным и не вызывает неприятия на ios.
                  Да, на сколько я понял у автора есть идеи написать отдельные стили/версию под самый последний Android L :)
                  И это будет круто!
                  2. Собственная реализация jquery содержит гораздо меньше строчек. Плюс не имеет встроеных штук для совместимости с старыми версиями IE и т.п.
                  Поэтому работает быстрее.
                  Да, там всего ~500 строчек, поэтому все достаточно понятно и читаемо. Да и весь основной функционал JQuery по работе с DOM присутствует.
                    0
                    Насчёт андроида это хорошо. В свете нового дизайна от Google вдвойне.

                    А насчёт jQuery вы меня не поняли.
                    Собственная реализация jquery содержит гораздо меньше строчек. Плюс не имеет встроеных штук для совместимости с старыми версиями IE и т.п.
                    Я и говорю, что есть кастомные jQuery, которые именно так и позиционируются. Есть Zepto,
                    есть современный jQuery 2, где выброшена поддержка старых браузеров, есть супер-минималистичный jqlite, который юзается под капотом Angular, к примеру.
                    Это мелочь, конечно, но зачем писать велосипед в столь развитой области.
                0
                Я использовал, сделал на нем прототипы двух приложений примерно полтора года назад. В то время было довольно сложно его кастомизировать, и были навязчивые баги с кроссплатформенностью (например, таб бар на андроиде имел белую полоску внизу высотой в 10px). Все ньюансы чинились сами, костылями. Я даже side menu свой написал.
                0
                Автор, а можно по-подробнее про то как компилировать это чудо js'a и html в xcode? А под ведро можно?
                Не совсем понял..(
                  0
                  Можно под что душе угодно собирать, что поддерживает WebView в котором можно отобразить HTML5 страничку. :)
                    0
                    PhoneGap из под вин не хочет устанавливать плагин для компилирования под iOS
                      0
                      Собирать iOS приложения можно только на Mac OS, насколько знаю. Исключения — всякие Xamarin'ы
                        0
                        Насколько я понял, автор коммента думает, что HTML+JS компилируется и получается нативный код, что в корне не верно. На выходе получаем приложение-обертку в которой главное окно — это «браузер» или WebView и в нее просто загружается HTML5 страничка. Доступ к файлам / камере получаем через нативные плагины.

                        > Автор, а можно по-подробнее про то как компилировать это чудо js'a и html в xcode?

                        Собирать приложение можно из командной строки: cordova build ios конечно же под Mac OS.

                        > А под ведро можно?

                        И под андроид тоже можно: cordova build android
                          0
                          Ааа. Вот оно что, я думал неужели получается хороший нативчик из этого всего и прям немог поверить.
                          А в компоненте просмотра страницы, соотвественно указывается локальное хранилище где лежит эти самые страницы? Или надо проводить товарища на сервер?
                  +1
                  Websql, sqlite есть для хранения данных? Какой максимальный объём?

                    0
                    caniuse.com/#feat=sql-storage

                    Но советую сильно не рассчитывать так как:
                    The Web SQL Database specification is no longer being maintained and support may be dropped in future versions.
                      0
                      Есть phonegap sqlite plugin:
                      github.com/brodysoft/Cordova-SQLitePlugin

                      Лично я использую пока что websql, так как она поддерживается во всех существующих и должна поддерживаться в девайсах, которые выйдут в ближайшее время :)
                      Если что — лучше писать весь свой websql код через свой некий уровень абстракции, что бы в случае прекращения поддержки можно было максимально просто перейти к примеру на sqlite плагин.
                    0
                    Обязательно приглашайте автора Framework 7, очень не хватает на Хабре хороших статей по этому инструменту. Вопрос только: кто может помочь с инвайтом?
                      0
                      Его профиль на хабре vladimirkharlampidi
                      Если у кого-то есть инвайт — его можно пригласить и думаю он сможет быть полезным сообществу :)
                        0
                        Думаю, написав какой-нибудь пост, он сможет стать полезным сообществу (для меня уже стал) и получит инвайт. Например про push-уведомления, авторизацию или особенности размещения в AppStore.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое