Комментарии 42
Мне кажется что ваш notice очень быстро превратится в непонятную кашу.
Недавно впервые столкнулся с большим количеством js в проекте и разделил его на 3 части:
— модули
— обработчики
— вспомогательные функции
Дальше написал метод автозагрузки обработчиков, который может выполнить набор функций в объекте.
Обработчики хранятся в отдельном объекте и делятся на группы, при загрузке страницы выполняется autoLoad, набор функций для всего сайта, далее при опр. действиях, либо на опр. страницах загружаются дополнительные обработчики.
Вот код который выполняет все функции в объекте.
Скоро напишу статью об этом.
Недавно впервые столкнулся с большим количеством js в проекте и разделил его на 3 части:
— модули
— обработчики
— вспомогательные функции
Дальше написал метод автозагрузки обработчиков, который может выполнить набор функций в объекте.
Обработчики хранятся в отдельном объекте и делятся на группы, при загрузке страницы выполняется autoLoad, набор функций для всего сайта, далее при опр. действиях, либо на опр. страницах загружаются дополнительные обработчики.
Вот код который выполняет все функции в объекте.
Project.load = function (obj) {
for (var handler in obj) {
var load = obj[handler]();
if (!load) continue;
if (load.init) {
load.init();
} else {
Project.init(load);
}
}
}
Скоро напишу статью об этом.
notice() в кашу не превращается, с чего бы? Обработка любого события в среднем занимает 3 строчки. Он может быть очень большим, это да. Но во-первых большой notice() != сложный notice(), его сложность практически не увеличивается от добавления новых событий. А во-вторых этот, даже большой, notice() — является почти идеальным изложением высокоуровневой логики всего приложения!
В этом его громадное преимущество перед динамической подпиской на события внутри модулей — либо вы чётко и явно видите что в какие моменты происходит в системе, либо всё это происходит неявно, под капотом. Явное всегда лучше неявного. К сожалению, иногда без динамической подписки на события обойтись нельзя, но эти ситуации бывают намного реже, чем принято думать.
В этом его громадное преимущество перед динамической подпиской на события внутри модулей — либо вы чётко и явно видите что в какие моменты происходит в системе, либо всё это происходит неявно, под капотом. Явное всегда лучше неявного. К сожалению, иногда без динамической подписки на события обойтись нельзя, но эти ситуации бывают намного реже, чем принято думать.
А для чего это нужно? Приведите пожалуйста пример проблемы которую удается решить этим, я сейчас не говорю о проблеме структурирования кода, это есть во всех ОО подходах к JS и у каждого он свой.
В front-end разработке обычно стараются максимально мало кода писать, а у вас тут основной метод растет с каждым новым событием.
Для отладки ведь можно добавлять console.log, а в готовом файле их просто убрать, тут же базируется вся архитектура на этом.
В front-end разработке обычно стараются максимально мало кода писать, а у вас тут основной метод растет с каждым новым событием.
Для отладки ведь можно добавлять console.log, а в готовом файле их просто убрать, тут же базируется вся архитектура на этом.
Проблемы? При чём тут конкретные проблемы, это простая разница между явным и не явным, и явное всегда лучше не явного (явное не всегда возможно, но когда возможно — всегда лучше).
Динамическая подписка на события автоматически означает несколько вещей, которые могут капитально осложнить отладку и понимание работы системы в целом:
Я знаю о чём говорю — моя текущая система написана именно в таком стиле — только масштабы другие, не одностраничный js, а куча сетевых сервисов в кластере, которые динамически друг друга находят и общаются. Так вот, несмотря на то, что эта моя система отлично работает, я предпочитаю видеть в коде статический диспетчер событий до тех пор, пока это возможно — ибо это намного проще.
Динамическая подписка на события автоматически означает несколько вещей, которые могут капитально осложнить отладку и понимание работы системы в целом:
- Нет полного списка всех возможных событий. Любой модуль может в любой момент начать генерировать новый вид событий, а другой модуль может начать их слушать. В результате без изучения всего кода всех модулей вы никогда не выясните, какие же вообще события бывают в вашей системе.
- Т.к. модули могут подписываться и отписываться от событий, то вы не можете быть уверены, что модуль гарантированно получит нужное событие. И если он не делает то, что должен был сделать после отправки этого события, то отладка может быть… интересной.
- Иногда возникают проблемы из-за того, чтобы несколько динамически зарегистрированных обработчиков события выполняются не в том порядке.
- и т.д.
Я знаю о чём говорю — моя текущая система написана именно в таком стиле — только масштабы другие, не одностраничный js, а куча сетевых сервисов в кластере, которые динамически друг друга находят и общаются. Так вот, несмотря на то, что эта моя система отлично работает, я предпочитаю видеть в коде статический диспетчер событий до тех пор, пока это возможно — ибо это намного проще.
Вот например модуль всплывающего окна, почему он был бы лучше используя вашу технологию?
Не подумайте пожалуйста что я тут пытаюсь вас потролить, просто мне действительно не понятно какие плюсы мне это дает на практике.
Не подумайте пожалуйста что я тут пытаюсь вас потролить, просто мне действительно не понятно какие плюсы мне это дает на практике.
MyProject.module.popUp = function (data) {
var $pp = $('<div class="project-popup"><a href="#" class="project-popup_close">Закрыть</a>'),
$bg = $('<div class="project-popup_backbg">');
function getBgDimenstions() {
return {
x: $(document).width(),
y: $(document).height()
}
}
function getPpPosition() {
return {
left: $(window).width() / 2 - $pp.width() / 2,
top: $(window).scrollTop() + ($(window).height() / 2) - $pp.height() / 2
}
}
function setPositions() {
var ppPos = getPpPosition(),
bgDim = getBgDimenstions();
$pp.css({ 'left': ppPos.left, 'top': ppPos.top });
$bg.width(bgDim.x).height(bgDim.y);
}
return {
isBuild: false,
el: {
pp: $pp,
bg: $bg
},
build: function () {
$('body').append($bg).append($pp.append(data));
var SELF = this;
$bg.click(function () {
SELF.hide();
});
$pp.find('.project-popup_close').click(function () {
SELF.hide();
return false;
});
},
show: function () {
if (!this.isBuild) {
this.build();
this.isBuild = true;
}
$('body').addClass('project-popup-overflow');
setPositions();
$pp.fadeIn();
$bg.fadeTo(300, 0.7);
$(window).bind('resize.popUp', function () {
setPositions();
});
},
hide: function () {
$pp.fadeOut();
$bg.fadeOut('normal', function () {
$('body').removeClass('project-popup-overflow');
});
$(window).unbind('resize.popUp');
},
destroy: function () {
$pp.remove();
$bg.remove();
}
}
}
Статья об архитектуре. Т.е. о том, как разделить функциональность приложения между модулями, и как потом их объединить чтобы они совместно реализовали функциональность полного приложения. В каком стиле написаны эти модули, какими библиотеками пользуются, и как они делают свою часть работы — с точки зрения описанной в статье архитектуры не имеет значения, делайте как Вам удобно.
Поэтому Ваш вопрос не имеет смысла — имея один модуль его никак нельзя «улучшить» описанным в статье подходом. Улучшить (потенциально, конечно) можно архитектуру приложения, для чего нужно видеть не один модуль, а все, плюс код «ядра», которых их объединяет и обеспечивает взаимодействие между этими модулями. При этом реализация самих модулей может практически не измениться, смотря насколько хорошо функциональность приложения разделена между модулями и насколько они изолированы друг от друга.
Поэтому Ваш вопрос не имеет смысла — имея один модуль его никак нельзя «улучшить» описанным в статье подходом. Улучшить (потенциально, конечно) можно архитектуру приложения, для чего нужно видеть не один модуль, а все, плюс код «ядра», которых их объединяет и обеспечивает взаимодействие между этими модулями. При этом реализация самих модулей может практически не измениться, смотря насколько хорошо функциональность приложения разделена между модулями и насколько они изолированы друг от друга.
А если есть несколько реализаций одного и того же виджета, и надо в зависимости от ситуации подгружать определенную реализацию. Вы предлагаете все эти реализации впихнуть в notice?
Так же при каждом чихе надо лезть в этот самый notice и там что-то делать, это очень неудобно.
Так же при каждом чихе надо лезть в этот самый notice и там что-то делать, это очень неудобно.
notice() занимается обработкой событий, а не подгрузкой конкретных реализаций. notice() знает, что при возникновении события A нужно создать новый объект класса B и добавить его в определённое место на страничке. Конкретная реализация класса B выбирается в других местах — либо на этапе подгрузки скриптов с сервера, либо класс B это на самом деле фабрика, и он сам выбирает подходящую реализацию в момент создания объекта.
Я не говорю что у вас плохая или неправильная архитектура, я с ней не работал, но с первого взгляда мне это кажется неудобным, и как писали выше приведет к каше, когда количество модулей будет приблежаться к тысяче (а вернее в несколько раз больше, потому как согласно вашей архитектуре каждый модуле будет разделен на несколько более простых виджетов).
Вот тут идет вполне конкретная реализация.
Я не совсем понимаю почему обработчик события должен выноситься из модуля (виджета), в какой-то общий компонент. На мой взгляд, все что должен сделать notice: поймать событие и оповестить всех кто на него подписан.
Ведь по сути на тот же case 'spellcheck: success' могут реагировать несколько виджетов, получается, что придобавлении нового модуля который работает с этим кейсом надо лезть в notice и что-то допедаливать. Изменилась концепция модуля — лезем в notice, переписываем.
С этим не согласен, когда файлик перевалит за пару тысяч строк, он перестанет быть изложением высокоуровневой логики всего приложения, а превратится в свалку, пусть и простую, но свалку.
Вот тут идет вполне конкретная реализация.
case 'spellcheck: success':
w.sum.add(data.spellerrors);
spellerrors[data.phrase] = data.spellerrors;
break;
Я не совсем понимаю почему обработчик события должен выноситься из модуля (виджета), в какой-то общий компонент. На мой взгляд, все что должен сделать notice: поймать событие и оповестить всех кто на него подписан.
Ведь по сути на тот же case 'spellcheck: success' могут реагировать несколько виджетов, получается, что придобавлении нового модуля который работает с этим кейсом надо лезть в notice и что-то допедаливать. Изменилась концепция модуля — лезем в notice, переписываем.
А во-вторых этот, даже большой, notice() — является почти идеальным изложением высокоуровневой логики всего приложения!
С этим не согласен, когда файлик перевалит за пару тысяч строк, он перестанет быть изложением высокоуровневой логики всего приложения, а превратится в свалку, пусть и простую, но свалку.
Я с трудом представляю тысячу разных модулей (не тысячу объектов одного класса, а именно тысячу разных классов) на одной страничке. Фактически это означает тысячу разных элементов UI, с которыми должен взаимодействовать пользователь. Вам этого пользователя не жалко? :) Я не уверен, что можно выделить тысячу таких виджетов даже на страничке GMail. Для таких больших проектов, как я и писал в статье, нужна полноценная архитектура предложенная Nicholas Zakas. Суть статьи в том, что большинство сложных приложений всё-таки не до такой степени сложны как GMail, и для них будет проще и нагляднее использовать описанный минималистский вариант этой архитектуры.
Тысяча модулей — легко в SAAS проектах.
Кстати GMail в принципе не особо большой проект и достаточно простой.
Если рассматривать как подход для более простых проектов, тогда это перетекает больше в вопрос идеологии, кому что больше по душе. Подойдет практически любая нормально структурированная архитектура.
Кстати GMail в принципе не особо большой проект и достаточно простой.
Если рассматривать как подход для более простых проектов, тогда это перетекает больше в вопрос идеологии, кому что больше по душе. Подойдет практически любая нормально структурированная архитектура.
Чем не устроил backbone.js?
Backbone.js не было 2 года назад, когда автор разработал свой фреймворк. И кстати у backbone нет глобального диспетчера событий (а очень не помешал бы).
что вы подразумеваете под «глобальным» диспетчером событий?
Зачем? backbone.view легко расширяется до обработки любых событий в документе, например генерируемых jquery.ui.
Вообще не понимаю, зачем реализовывать свою систему событий вместо предоставляемой jquery.
Вообще не понимаю, зачем реализовывать свою систему событий вместо предоставляемой jquery.
Я выше в комментариях объяснял, зачем. Если в какой-то момент понадобятся динамические обработчики событий, то, разумеется, нет смысла что-то своё изобретать, вполне подойдут события jQuery. Но статический диспетчер всё сильно упрощает, поэтому (пока его возможностей хватает) нет смысла усложнять систему без нужды.
Скажите, а насколько легко вы в процессе принимаете решения о способе реализации тех или иных фич? Не возникает ли сложностей с выбором из пяти альтернативных вариантов реализации?
И правильно ли я понял, что предлагаемая вами концепция виджетов чем-то похожа на jquery.ui, когда на один элемент можно навесить и draggable, и resizable и пр.?
И правильно ли я понял, что предлагаемая вами концепция виджетов чем-то похожа на jquery.ui, когда на один элемент можно навесить и draggable, и resizable и пр.?
Принимать решения всегда непросто. :) Я стараюсь писать код как можно проще, что в свою очередь обычно означает что не будет универсальным и типовым. Т.е. из N вариантов приходится выбирать каждый раз разные, потому что ситуации/приложения тоже каждый раз разные, и самым простым решением в одной конкретной ситуации оказывается не то же решение, что в другой ситуации.
Могу привести пример такой ситуации даже с ключевыми элементами архитектуры: в некоторых приложениях проще оказывается держать в модулях/виджетах только логику, а все дизайнерские/оформительские действия выполнять в notice(); а в других приложениях проще писать модули с жёстким ограничением что в их часть DOM никто снаружи не лазит, и все дизайнерские/оформительские операции над модулем реализуются у него внутри (в существующих и/или специально созданных новых методах).
Насчёт jQuery.UI — врядли. Виджеты о которых писал я не предназначены для совместного навешивания на один и тот же элемент DOM. Виджет является единственным владельцем какого-то блока DOM, и максимум может делегировать части своего блока вложенным виджетам — но в этом случае он сам уже в эти части не лазит (если нужно — дёргает методы вложенного виждета которому он делегировал эту часть своего блока).
Могу привести пример такой ситуации даже с ключевыми элементами архитектуры: в некоторых приложениях проще оказывается держать в модулях/виджетах только логику, а все дизайнерские/оформительские действия выполнять в notice(); а в других приложениях проще писать модули с жёстким ограничением что в их часть DOM никто снаружи не лазит, и все дизайнерские/оформительские операции над модулем реализуются у него внутри (в существующих и/или специально созданных новых методах).
Насчёт jQuery.UI — врядли. Виджеты о которых писал я не предназначены для совместного навешивания на один и тот же элемент DOM. Виджет является единственным владельцем какого-то блока DOM, и максимум может делегировать части своего блока вложенным виджетам — но в этом случае он сам уже в эти части не лазит (если нужно — дёргает методы вложенного виждета которому он делегировал эту часть своего блока).
Подскажите как через jQuery отловить например встроенное событие backbone «change:attribute» не дублируя его методами jQuery?
События backbone не являются событиями документа, если я не ошибаюсь.
Возможно, а отлавливать их нужно. И то как сейчас backbone предлагает сделать (например передавать линки на вью в модели и/или обратно через конструкторы) — не очень удобно. А был бы родной бекбоновский глобальный диспетчер событий — ловить model.save() во вью было бы можно простой однострочной декларацией в events вью без лишних выкрутасов.
Во-первых, не надо путать Backbone.Events и события DOMа, это разные вещи. А что касается линков на модели, в чем проблема? View служит для формирования отображения модели, так что иметь ссылку на модель просто обязана, и все легко делается как раз простой однострочной декларацией this.model.bind('change', this.render, this); в методе initialize.
Не понял почему мне адресовано пожелание не путать события backbone и DOM, если я говорю только о нативных событиях backbone, но это неважно. Проблемы с передачей линков нет — костыль как костыль. Но еще раз — если бы был глобальный диспетчер backbone, линки можно было бы не передавать вообще, что сделало бы код лаконичнее и без костылей.
var eventEmitter = _.extend({}, Backbone.Events);
eventEmitter.bind('msg', function(msg){ console.log(msg) });
eventEmitter.trigger('msg', 'hello world');
не катит?
Хочется уточнить по поводу заворачивания статуса запроса в — разве replaceWith не решает описанной в тексте проблемы?
Нет, не решает. Я, безусловно, в магию верю. Но здесь довольно безнадёжная ситуация: есть объект (с тэгами) на который есть две ссылки (одна в DOM, вторая в this.$). Заменить этот объект другим (доступным по обоим ссылкам) используя только одну из ссылок невозможно — нужен дополнительный indirection level (ссылка на ссылку). Как писал Спольски, все программисты делятся на две группы — одни понимают ссылки, вторые нет. :-/ В данном случае я бы хотел их временно не понимать, и чтобы всё как-нить магически заработало. ;)
Мне почему то казалось что в this.$ как объект jQuery должна быть ссылка на DOM.
Разве проблема не в том что используется this.$.html(«далее идет строка с HTML кодом») вместо this.$.replaceWith? Руки чешутся проверить, но время то позднее :)
Разве проблема не в том что используется this.$.html(«далее идет строка с HTML кодом») вместо this.$.replaceWith? Руки чешутся проверить, но время то позднее :)
(ниже — псевдокод)
Нет, так не получится. У нас есть ссылка на "
Нет, так не получится. У нас есть ссылка на "
div#foo
" в this.element
и он же в DOM. Делаем так: $('div#foo').replaceWith('div#bar')
, в DOM объект заменяется, а вот в this.element
ссылка ведёт на "div#foo
", которого даже нету на странице.Допустим так, тогда поясните как у автора работает смена html кода после заворачивания его в span.
Видимо, меняется содержимое тега, на который у нас ссылка.
Этот span добавляет тот самый необходимый indirection level. С ним в DOM ссылка на span, а уже внутри span ссылка на отрендерённый шаблон. В this.$ тоже ссылка на span.
this.$.html() заменяет ссылку внутри span, после него span указывает на совершенно другой отрендерённый шаблон. При этом сам span не заменялся, так что ссылки на него и в DOM и в this.$ по-прежнему корректны.
this.$.html() заменяет ссылку внутри span, после него span указывает на совершенно другой отрендерённый шаблон. При этом сам span не заменялся, так что ссылки на него и в DOM и в this.$ по-прежнему корректны.
Вот так вот берешь проект, открываешь такой маленький js файлик, чтобы просто поменять, например, какое-нить название поля…
А потом сидишь два дня разбираешся откуда гребанная ошибка выскакивает.
А потом сидишь два дня разбираешся откуда гребанная ошибка выскакивает.
А как в вашей архитектуре может быть реализована функция wysiwyg-редактора «выделение текста» -> «удаление выделенного текста» (удаление с помощью кнопки на панели либо нажатием клавиши Del)?
Cерия статей в тему на knockout.js. Правда, на английском.
Имхо ваш notice — это Контроллер, а «события» — это его методы. Если ваше приложение — это одна страница (именно как контекст, а не с точки зрения загрузки с сервера одного html), то все логично. Одна страница — один контроллер. А вот если в приложении появится несколько контекстов, то едининый контроллер смотрится странно. Думаю это и имели в виду предыдущие комментаторы, когда говорили про «будет каша».
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Простая минималистская реализация сложных JavaScript приложений