View
125
Download
2
Category
Preview:
DESCRIPTION
Автор фреймворка jWidget рассказывает о причинах его появления и внутренней организации.
Citation preview
Егор Непомнящих
Очередной MV* фреймворк?
Зачем?
TodoMVC.com
Так зачем?
● Когда я начал разработку своего фреймворка, об остальных ничего не было слышно
Так зачем?
● Когда я начал разработку своего фреймворка, об остальных ничего не было слышно
● А как оказалось в итоге, jWidget круче всех
Приложения на базе jWidget
Приложения на базе jWidgetwww.digmydata.com
Приложения на базе jWidgetМой entry для Ludum Dare
Приложения на базе jWidget
Отличительные черты jWidget
● ООП-подход
Отличительные черты jWidget
● ООП-подход● Гранулярность
Отличительные черты jWidget
● ООП-подход● Гранулярность● Предсказуемость
Отличительные черты jWidget
● ООП-подход● Гранулярность● Предсказуемость● Простота
Отличительные черты jWidget
● ООП-подход● Гранулярность● Предсказуемость● Простота● Скорость
Тест на скорость
Вот только часть решений не MV* (читеры)
Что такое MV*?
Что такое MV*?
Приложение
Приложение
Что такое MV*?
Сущность 1
Сущность 2
Сущность 3
LibreOffice Impress
Что такое MV*?
Презентация
Слайд
Элемент слайда
LibreOffice Impress
Что такое MV*?
Модель презентации
Модель слайда
Модель элемента слайда
Панель слайдов
Диалог свойств слайда
Данные Представление
Редактор слайда
Представление элемента
Просмотрщик слайдов
LibreOffice Impress
Что такое MV*?
Модель презентации
Модель слайда
Модель элемента слайда
Панель слайдов
Редактор слайда
Представление элемента
Просмотрщик слайдов
Диалог свойств слайда
Данные Представление
LibreOffice Impress
Что такое MV*?
Модель презентации
Модель слайда
Модель элемента слайда
Панель слайдов
Редактор слайда
Представление элемента
Просмотрщик слайдов
Диалог свойств слайда
Данные Представление
События
События
События
Когда стоит использовать клиентские MV*-фреймворки?
● Крупное приложение с большим количеством взаимосвязанных сущностей
● Есть сущности, имеющие большое количество представлений одновременно
● В команде достаточно матерых JavaScript-разработчиков
Когда не стоит использовать клиентские MV*-фреймворки?
● В команде мало хороших JavaScript-разработчиков
● Страница должна грамотно парситься поисковыми роботами и аналитическими системами
● Вам нужна хорошая скорость работы сайта (jWidget – исключение!)
Документация
Найдите ссылку в проекте на GitHub
https://github.com/enepomnyaschih/jwidget
Структура фреймворка
6 слоев
Структура фреймворка
1. Классы и объекты (JW.Class)
Классы и объекты
На базе популярной библиотекиJohn'а Resig'а “Simple JavaScript inheritance”.
http://ejohn.org/blog/simple-javascript-inheritance/
Классы и объекты
Система агрегации объектов.var Soldier = function() {
Soldier._super.call(this);
this.leftHand = this.own(new Hand("left"));
this.rightHand = this.own(new Hand("right"));
};
JW.extend(Soldier, JW.Class);
var soldier = new Soldier();
soldier.destroy();
// руки уничтожены тоже
Структура фреймворка
1. Классы и объекты (JW.Class)
2. События (JW.Event)
События
Нет никаких Observable. Есть Event.this.titleChangeEvent = this.own(new JW.Event());
this.titleChangeEvent.trigger(
new JW.ValueEventParams(this, title));
this.own(obj.titleChangeEvent.bind(
this._onTitleChange, this))
Подписка.
Структура фреймворка
1. Классы и объекты (JW.Class)
2. События (JW.Event)
3. Свойства (JW.Property)
Свойства
Интерфейс проще некуда:● Метод get()● Метод set(value)● Событие changeEvent
Свойства
Автоматическое дублирование.
source target1 target2
var source = new JW.Property("apple");
var target1 = new JW.Property();
target1.bindTo(source);
var target2 = new JW.Property();
target2.bindTo(target1);
source.set("banana");
// target2.get() === "banana"
Свойства
Функтор формирует свойство по формуле.
target '{value} {unit}'JW.Functor
target == '1000 MW' value == 1000, unit == 'MW'
target == '1500 МВ' value == 1500, unit == 'МВ'
Свойства
Функтор формирует свойство по формуле.
target '{value} {unit}'
var target = new JW.Functor( [ value, unit ], function(value, unit) { return value + " " + unit; }, this).target;
JW.Functor
Свойства
Switcher переключает on/off.
Hash (#doc_id) view selected? JW.Functor JW.Switcher
http://myapp.com/#5077-MB
Свойства
Switcher переключает on/off.
Hash (#doc_id) view selected? JW.Functor JW.Switcher
this.own(new JW.Switcher([view], {
init: function(view) { view.selected.set(true); },
done: function(view) { view.selected.set(false); },
scope: this
}));
Свойства
Можно осуществлять Data binding.var el = $("#document");
this.own(new JW.UI.ClassUpdater(
el, "my-selected", selected));
this.own(new JW.UI.TextUpdater(el, name));
this.own(new JW.UI.ValueUpdater(el, name));
this.own(new JW.UI.ValueListener(el, name));
И двусторонний Data binding.
Свойства
Можно налету менять локализацию.
var printText = this.own(locale.getFunctor("print")).target;
this.own(new JW.UI.TextUpdater(el, printText));
Структура фреймворка
1. Классы и объекты (JW.Class)
2. События (JW.Event)
3. Свойства (JW.Property)
4. Коллекции (JW.AbstractCollection)
Коллекции
Три коллекции:● Массив
(JW.AbstractArray)● Словарь
(JW.AbstractMap)● Множество
(JW.AbstractSet)
Коллекции
Три коллекции:● Массив
(JW.AbstractArray)● Словарь
(JW.AbstractMap)● Множество
(JW.AbstractSet)
Две реализации:● JW.Array
JW.MapJW.Set
● JW.ObservableArrayJW.ObservableMapJW.ObservableSet
Коллекции
И самое главное...
Коллекции
И самое главное...
СИНХРОНИЗАТОРЫ
Коллекции
Конвертер элементов (Mapper)
target[i] function(source[i])
var target = dataCollection.map(function(data) { return new View(this, data);}, this);
Коллекции
Конвертер элементов (Mapper)
target[i] function(source[i])
var target = dataCollection.map(function(data) { return new View(this, data);}, this);
Коллекции
Конвертер элементов (Mapper)
target[i] function(source[i])
var mapper = dataCollection.createMapper({ createItem: function(data) { return new View(this, data); }, destroyItem: JW.destroy, scope: this});var viewCollection = mapper.target;
Коллекции
Конвертер элементов (Mapper)
target[i] function(source[i])
var mapper = dataCollection.createMapper({ createItem: function(data) { return new View(this, data); }, destroyItem: JW.destroy, scope: this});var viewCollection = mapper.target;
Коллекции
Конвертер элементов (Mapper)
target[i] function(source[i])
var source = new JW.ObservableArray([5]);var target = source.createMapper({ createItem: function(item) { return 2 * item; }});console.log(target.get(0)); // 10
source.add(7);console.log(target.get(1)); // 14
Коллекции
Другие синхронизаторы
1. Конвертер элементов (Mapper)
target[i] function(source[i])
2. Фильтровщик (Filterer)
target[ ] function(source[ ]) ? source[ ] :
Коллекции
Другие синхронизаторы
1. Конвертер элементов (Mapper)
target[i] function(source[i])
2. Фильтровщик (Filterer)
target[ ] function(source[ ]) ? source[ ] :
3. Конвертер в множество (Lister)
JW.Set target[ ] source[ ]
Коллекции
Другие синхронизаторы
4. Конвертер в массив (Orderer)
JW.Array target (в порядке доб.) source
Коллекции
Другие синхронизаторы
4. Конвертер в массив (Orderer)
JW.Array target (в порядке доб.) source
5. Сортировщик (SorterComparing)
JW.Array target (sorted by f(x, y)) source
Коллекции
Другие синхронизаторы
4. Конвертер в массив (Orderer)
JW.Array target (в порядке доб.) source
5. Сортировщик (SorterComparing)
JW.Array target (sorted by f(x, y)) source
6. Объединитель массивов (Merger)
target source1 … sourceN
Коллекции
Другие синхронизаторы
7. Обратитель массива (Reverser)
target[i] source[length - i – 1]
Коллекции
Другие синхронизаторы
7. Обратитель массива (Reverser)
target[i] source[length - i – 1]
8. Индексатор (Indexer)
target[function(source[ ])] source[ ]
Коллекции
Можно делать цепочки синхронизаторов.
JW.Set JW.Array
JW.Set source list
JW.Map index
Sorter
Filterer
Indexer
Структура фреймворка
1. Классы и объекты (JW.Class)
2. События (JW.Event)
3. Свойства (JW.Property)
4. Коллекции (JW.AbstractCollection)
True
Структура фреймворка
1. Классы и объекты (JW.Class)
2. События (JW.Event)
3. Свойства (JW.Property)
4. Коллекции (JW.AbstractCollection)
5. Компоненты (JW.UI.Component)
Структура фреймворка
1. Классы и объекты (JW.Class)
2. События (JW.Event)
3. Свойства (JW.Property)
4. Коллекции (JW.AbstractCollection)
5. Компоненты (JW.UI.Component) ReactJS
И все-таки...
Компонентыvar Greeter = function() { Greeter._super.call(this); this.name = this.own(new JW.Property("wanderer"));};JW.extend(Greeter, JW.UI.Component, { renderNameField: function(el) { this.own(new JW.UI.ValueUpdater(el, this.name)); this.own(new JW.UI.ValueListener(el, this.name)); }, renderGreeting: function(el) { var text = this.own(new JW.Functor([this.name], function(name) { return "Hello, " + name + "!"; }, this)).target; // build greeting message this.own(new JW.UI.TextUpdater(el, text)); }});JW.UI.template(Greeter, { main: '<div class="greeter">' + '<p>Your name: <input jwid="name-field"></p>' + '<div jwid="greeting"></div>' + '</div>'});new Greeter().renderTo("body");
Компоненты<div jwclass="greeter"> <p>Your name: <input jwid="name-field"></p> <div jwid="greeting"></div></div>
<div class="greeter"> <p>Your name: <input class="greeter-name-field"></p> <div class="greeter-greeting"></div></div>
Компоненты
Шаблонный движок JW.UI ну
ОООООЧЕНЬ
Быстрый.
Быстрее даже,
чем чистый HTML.
Компоненты
Шаблонный движок JW.UI ну
ОООООЧЕНЬ
Быстрый.
Быстрее даже,
чем чистый HTML.
Компоненты
Как это возможно?● Каждый HTML-шаблон рендерится только
один раз● После этого каждый новый компонент
получается клонированием отрендеренного фрагмента
● Шаблон рендеритсянапрямую браузером, безвсякого препроцессинга
● Никаких селекторов
Компоненты
renderDocument: function(el) {
return this.own(new DocumentView(this.document));
}
<div jwclass="my-component">
<div jwid="document"></div>
</div>
DocumentView
Компоненты
renderDocument: function(el) {
return this.own(new JW.Mapper([ this.document ], {
createValue: function(document) {
return new DocumentView(this.document);
}
destroyValue: JW.destroy,
scope: this
})).target;
}
<div jwclass="my-component">
<div jwid="document"></div>
</div>
DocumentView
Компоненты
renderDocuments: function(el) {
return this.documents.$map(function(document) {
return this.own(new DocumentView(document));
}, this);
}
<div jwclass="my-component">
<div jwid="documents">
</div>
</div>
DocumentView
DocumentView
DocumentView
DocumentView
КомпонентыrenderDocuments: function(el) {
return this.own(this.documents.createMapper({
createItem: function(document) {
return new DocumentView(document);
},
destroyItem: JW.destroy,
scope: this
})).target;
}
<div jwclass="my-component">
<div jwid="documents">
</div>
</div>
DocumentView
DocumentView
DocumentView
DocumentView
Компоненты
renderDocument: function(el) {
return false;
}
<div jwclass="my-component">
<div jwid="document"></div>
</div>
Компоненты
renderDocument: function(el) {
this.own(new JW.UI.TextUpdater(el, this.text));
}
<div jwclass="my-component">
<div jwid="document"></div>
</div>
Компоненты
JW.UI.Component vs ReactJS (TBD)
● Совместим с jQuery
● Можно создавать поверх существующего DOM
● Прозрачнее
● Дает полный контроль над компонентамии DOM
● Ниже порог входа(проще на старте)
● Код проще
● Большое сообщество разработчиков
Структура фреймворка
1. Классы и объекты (JW.Class)
2. События (JW.Event)
3. Свойства (JW.Property)
4. Коллекции (JW.AbstractCollection)
5. Компоненты (JW.UI.Component)
6. Система сборки (jWidget SDK)
Система сборки
Standalone проект
jWidget SDK
https://github.com/enepomnyaschih/jwsdk
Система сборки
Standalone проект
jWidget SDK
https://github.com/enepomnyaschih/jwsdk
Является прямой альтернативой GruntJS,
и посему планируется заменить
jWidget SDK плагином к GruntJS
И все же...
Система сборки
1. Формируем пакеты
{
"requires": [
"thirdparty/jwquery.js|auto",
"thirdparty/jwlib.js|auto",
"thirdparty/jwui.js|auto"
],
"resources": [
"mt/mt.js",
"mt/data/data.js",
"mt/data/tweet.js",
...
]
}
Система сборки
2. Создаем страницы
{
"package": "mt",
"template": "base",
"title": "Mini-Twitter"
}
Система сборки
3. Собираем
илиjwsdk debug jwsdk-config
jwsdk release jwsdk-config
Система сборки
Супер-фича: конвертирование шаблонов в JS
{
"resources": [
"mt/app/app.js",
"mt/app/app.jw.html : mt.App",
...
]
}
<div jwclass="mt-app">
<div jwid="profile"></div>
<div jwid="tweets"></div>
</div>
JW.UI.template(mt.App,{main:'<div jwclass="mt-app"><div jwid="profile"></div><div jwid="tweets"></div></div>'});
Система сборки
Также поддерживаются другие форматы:● html● json● txt● less● sass● scss● styl● jsx (TBD)
Система сборки
Выполняются оптимизации:● Объединение и минимизация JS
Система сборки
Выполняются оптимизации:● Объединение и минимизация JS● Параметр timestamp для браузерного
кэширования
Система сборки
Выполняются оптимизации:● Объединение и минимизация JS● Параметр timestamp для браузерного
кэширования● Конвертирование малых изображений и
шрифтов в base64 Data URI
Система сборки
Выполняются оптимизации:● Объединение и минимизация JS● Параметр timestamp для браузерного
кэширования● Конвертирование малых изображений и
шрифтов в base64 Data URI● Поддержка динамической загрузки пакетов
по требованию
Совместимость jWidget с другими библиотеками и фреймворками
● Весь функционал размещен в отдельном пространстве имен JW
Совместимость jWidget с другими библиотеками и фреймворками
● Весь функционал размещен в отдельном пространстве имен JW
● UI-слой работает на базе jQuery
Совместимость jWidget с другими библиотеками и фреймворками
● Весь функционал размещен в отдельном пространстве имен JW
● UI-слой работает на базе jQuery● Разрабатываются адаптеры между моделью
jWidget и UI-слоями других фреймворков (например, ReactJS и ExtJS)
Ссылки
Twitter: @jwidgetproject
Почта: jwidgetproject@gmail.com
GitHub (там ссылка на документацию): https://github.com/enepomnyaschih/jwidget
Статья на хабрахабре (там еще ссылки): http://habrahabr.ru/post/219995/
Вопросы?
FAQ● jWidget – почему так называется?
Потому что гладиолус● Много ли пользователей у jWidget?
Пока нет● Почему ООП-подход так важен?
Легко переносится на Dart и пр. платформы● Есть ли готовая библиотека визуальных
компонентов?Нет. Но легко совмещается с jQuery UI
Recommended