Сама библиотека работает как подстройка к jQuery. Практически зависимость от jQuery условна и ее можно разделить с самой библиотекой, пока не было необходимости.
Jiant позволяет создавать клиентские приложения любой сложности, значительно упрощая их поддержку и развитие.
Краткая предыстория
Я более 10 лет занимаюсь разработкой веб приложений. Последние пару лет постоянно использовал GWT. К сожалению, Google не смог решить самую серьезную проблему этой библиотеки — медленную компиляцию. После длительной паузы в выходе новых версий (между 2.4 и 2.5) и передачи GWT на сторону, ведущие разработчики компании где я работаю (не широко известной) сошлись во мнении что с GWT пора завязывать. Мы решили дальше вести разработку на jQuery. Но GWT привило несколько очень удобных «рефлексов» разработки, которых явно не хватало при чистом javascript программировании. В частности, MVC подход с разделением на презентацию и логику, EventBus. Неудобство и дискомфорт нового положения привели к некоторым идеям, которые постепенно развиваясь и трансформируясь привели к решению, описанному ниже.
Также до написания библиотеки я разбирался с существующими MVC решениями типа Backbone и был не очень счастлив количеством boilerplate кода(избыточностью требуемых объявлений).
Бенефиты
Собственно, чтобы был стимул читать, забегу вперед и перечислю основные плюсы:
- Автозавершение кода (autocomplete) — вместо строковых объявлений.
- Полное абстрагирование интерфейса и разделение логики и представления
- Валидация привязки javascript переменных к собственно элементам веб страницы во время запуска приложения, а не во время исполнения конкретного фрагмента кода. Что дает дополнительную защиту разработчику функционала от шаловливых рук дизайнера
- Самодокументирование на уровне кода максимально простым способом — меньше и в то же время понятней мне не встречалось
- Простота встраивания — в любой момент в любое место можно встроить и немедленно получить плюсы
И еще много всякого по мелочи.
Принципы
Идеология библиотеки строится на следующий тезисах. Если принять их за основу, то все дальнейшее понимается легко:
- Любой интерфейс состоит из набора вьюшек (View, Вид?) — по русски слово подобрать сложно, так как это не виджеты, но и не экраны. Это уникальные значащие части интерфейса. Проще всего их выделить, если просто слушать рассказ заказчика: вот тут «экран редактирования контакта», отсюда переходим на «список пользователей» (на странице может быть несколько видимых View одновременно).
- Каждая View содержит в себе набор элементов, типа «кнопка возврата на список», «контейнер списка пользователей», «картинка пользователя». Эти элементы идентифицируемы в рамках одной View
А также несколько технологических тезисов, которые будут вести через дебри, когда надо принять решение «а как это сделать»? Так, чтобы не противоречило следующему:
- То что видит пользователь в итоге на экране это по сути дела html код. А что лучше всего отрисует html? Только сам html. То есть когда нам надо показать пользователю некий интерфейс — мы просто помещаем его html на страницу и используем когда нужно. Вместо генерации через DOM манипуляцию или конкатенирование строк на лету (кроме самых простых случаев). Это также облегчит жизнь дизайнера — он просто отдизайнит html код.
- Повторяющиеся элементы генерируем шаблонами, которые опять же есть просто html код на странице
Самое важное — Jiant (javascript interface abstract notation) — это больше философия разработки чем набор трюков. Если разрабатывать придерживаясь этой философии, то все получается легко и естественно.
View
Проектируем
Итак, вначале проектируем интерфейс на json, не вдаваясь в детали реализации. Например, просто слушая рассказчика (или заказчика) о форме регистрации: «Пусть пользователь вводит имя и емейл, потом жмет кнопку зарегистрироваться и ему показывается окошко что он зарегистрирован, на которое он нажмет и попадет внутрь».
Описываем это на json, как слышим так и пишем.
var registration = { //объявим одну переменную, чтобы не засорять глобальное пространство имен views: { // секция views содержит те самые концептуальнофилософские View registrationFormView: { // форма регистрации nameInput: jiant.input, // поле ввода имени emailInput: jiant.input, // поле ввода емейла registerCtl: jiant.ctl // элемент интерфейса, нажав на который пользователь зарегистрируется }, successView: { // экран с сообщением об успехе continueCtl: jiant.ctl // кнопка или ссылка, неважно, для входа в приложение }, errorView: { // экран с сообщением об ошибке errorMessage: jiant.label, // сообщение об ошибке disposeCtl: jiant.ctl // кнопка чтобы спрятать сообщение об ошибке и поменять значения в полях ввода } } };
Как это все понять? Читаем код следующим образом: «наше приложение состоит из трех вьюшек — формы регистрации, сообщения об успехе и сообщения об ошибке. Форма регистрации содержит два поля ввода и один контрол …» и так далее. jiant.input и jiant.ctl здесь служат только для целей документирования. Например, можно написать так и это тоже будет работать, но будет менее понятно при чтении:
var registration = { views: { registrationFormView: { nameInput: 1, emailInput: 1, registerCtl: 1, }, successView: { continueCtl: 1 }, errorView: { errorMessage: 1, disposeCtl: 1 } } };
Реализуем
Теперь, когда интерфейс описан в высшей степени абстрактно и максимально кратко — мы реализуем логику работы приложения с интерфейсом и реализуем сам интерфейс в html коде. В итоге получая MVC в форме html-json-javascript. Вначале реализуем сами вьюшки на html, по следующим правилам:
- Каждой View соответствует элемент html с соответствующим id-ом
- Каждому элементу View (nameInput, registerCtl) соответствует элемент html с соответствующим классом, расположенный внутри элемента View
Реализация registrationFormView:
<div id="_registrationFormView"> Your name: <input class="_nameInput"> <br> Your email: <input class="_emailInput"> <br> <button class="_registerCtl">Register</button> </div>
Что за подчерк перед идентификатором и именем класса? Чуть ниже он объясняется.
Аналогично реализуем successView:
<div id="_successView"> Поздравляем с регистрацией, мы Вам очень рады! <button class="_continueCtl">Continue</button> </div>
и errorView.
Воплощаем абстракцию
Итак, у нас есть абстрактное определение интерфейса. Есть его реализация. Как их связать?
Следующим образом:
$(function() { jiant.bindUi("_", registration, true); });
Готово.
Функция bindUi принимает первым параметром префикс, который добавляется к каждому абстрактному определению View или элемента View. В данном случае это подчерк. Если передать пустую строку, то имена в json и идентификаторы-классы в html будут совпадать. Я нахожу удобным использовать подчерк, для повышения читаемости html кода — сразу видно значащие элементы интерфейса.
Второй параметр — это переменная, содержащая абстрактное определение приложения. jiant достает из нее элемент views и связывает каждую вьюшку с ее реализацией.
Третий параметр — это включение отладочного режима, что упрощает поиск ошибок и увеличивает количество вывода в консоль.
Магия
Здесь уже начинается немного магии. Во время bindUi выполняется поиск каждой View из определения интерфейса по идентификатору. Затем результирующий jQuery wrapper объект присоединяется к переменной определения. То есть — после выполнения bindUi — переменная registration.views.registrationFormView содержит в себе результат $("#_registrationFormView"). Также каждый элемент вьюшки ищется по классу и заполняется, то есть registration.views.registrationFormView.registerCtl содержит в себе $("#_registrationFormView").find("._registerCtl").
Для каждой не найденной ссылки — сообщается ошибка в консоль. И если включен отладочный режим — в конце выдается алерт со списком всех ошибок, чтобы разработчик точно не упустил это из виду.
На выходе мы имеем полностью привязанный к настоящим html элементам объект описания интерфейса. Который теперь можно использовать в коде логики следующим образом.
Используем
Самый простой способ — пишем свой отдельный javascript файл и регистрируемся на событие onUiBound, например:
jiant.onUiBound(function($, app) { // в этот момент все jQuery wrapper объекты присоединены к абстрактному объявлению интерфейса var view = app.views.registrationFormView, errorView = app.views.errorView; // чтобы меньше писать view.registerCtl.click(function() { errorView.hide(); if (view.nameInput.val() == "") { errorView.errorMessage.html("Пустое имя!"); errorView.show(); } else if (view.emailInput.val() == "") { errorView.errorMessage.html("Пустой емейл!"); errorView.show(); } else { view.hide(); app.views.successView.show(); } }); });
Готова логика. При чтении это не ощущается, но в хорошем IDE — 75% этого кода авто-завершено, т.е. сэкономлено время на написании и уменьшена вероятность ошибки из-за опечатки.
Что еще
Со временем в процессе использования добавлены шаблоны, EventBus, хэш-навигация, аякс работа с сервером — все это в форме максимально дружественной к автокомплиту и требующей минимальных усилий от разработчика.
Пример аякс определения:
var app = { ajax: { getDataFromServer: function(id, callback) {} } }
Больше от разработчика ничего не требуется — jiant сам реализует код аякс запроса.
Остается пользоваться:
app.ajax.getDataFromServer(6345, function(data) { //do smth with data });
Кто знает GWT — сразу увидит полное сходство с Async сервисом.
Дальше
Если эта статья выйдет в печать и вызовет интерес, я готов дальше расписать остальные функции (шаблоны, аякс, состояния и шину событий). Код проекта на gihub: github.com/vecnas/jiant, сайт с описанием: sites.google.com/site/jiantscript/home
ссылка на оригинал статьи http://habrahabr.ru/post/176473/
Добавить комментарий