Pull to refresh

Comments 47

Спасибо за перевод, интересно.

А я сейчас отказался от «традиционных» MV* подходов и пробую построить фронтенд-архитектуру по принципу «Modular Javascript» от Закаса.

Пока что получается нечто, с одной стороны похожее на Яндексовый БЭМ. А с другой не похожее. :)
А как модульность конфликтует с MV*-подходами? Или в том скринкасте про какие-то особые другие модули? Компоненты приложений Backbone/Marionette/итд обязательно надо оформлять в виде каких-либо модулей (например, RequireJS/AMD).
Никак не конфликтует. Просто в случае «modular js» MV* спускается с уровня всего приложения на уровень модулей.

Основной смысл – отсутствие вообще какой-либо связности между модулями и отсутствие какой-либо бизнес-логики вне модулей.

Грубо говоря, как я это понимаю: MV* подходы дают вам некий каркас с «пустыми местами» для модулей, а «modular js» полностью строится из модулей, включая сам каркас.
Не согласен. У нас вполне успешно используются AMD-модули (require.js) в проектах с Backbone/Marionette, Backbone/Thorax (Backbone/Chaplin, кстати, вообще обязывает использовать модули), Angular, да и вообще в любом проекте в последнее время. На мой взгляд, модули позволяют добиться инкапсуляции, изоляции, независимости между компонентами, а MV*-фреймворки задают структуру самого приложения и определяют (внимание) семантику этих самых модулей. Как любой компонент, так и Backbone-приложение можно написать как олдскульно, так и используя модули. Так что «модули» и «фреймворки» не противоречат, а отлично дополняют друг друга.
«Использовать модули» != «modular js».
А в чем разница? Модули — сам концепт. Понятное дело, что использование того или иного формата (AMD/CommonJS) или загрузчика (RequireJS, curl.js, etc) не гарантирует, что приложение концептуально «modular» (однако, best practices по использованию модулей — однозначное использование module-паттернов, иначе какой от них смысл). А что еще?
отсутствие вообще какой-либо связности
это надо понимать, что и библиотеки типа jQuery переопределяются у вас в каждом модуле? Интересует, используете ли вы некоторый общий код в базовом модуле, чтобы не повторять его в каждом? И как его переносите, если используете?
Каждый модуль не знает ничего про jQuery, он знает только про свою песочницу. Каждая песочница знает только про свой модуль и про ядро. Ядро знает только про песочницы и библиотеки (jQuery).

Что такое «базовый модуль»?
«базовый модуль» — это то, что в Ваших терминах — ядро. Стало быть, некоторая связность есть, на уровне поставки библиотек. Я себе систему представляю как базовый модуль с либами, который может дополняться другими базовыми модулями с новыми либами или плагинами — это дань оптимизации их загрузки. Модули несут функциональность так, что их может быть достаточно. Но есть и легковесные модули, имеющие только движок событийной связи (postMessage, Custom Event) и чтения либ. В общем, то же самое, только песочницами выступают скоупы модулей.
Спасибо, посмотрел. Странное выражение на стр. 100 — «Модули ничего не знают о ядре». А либы? Если модули хотят использовать джиквери?
Насколько я понял, предполагается, что песочница модуля предоставляет ему все необходимые методы (и одновременно контролирует, чтобы модуль не лез к чужим DOM-элементам, например).
>> промышленного Rails приложения
Объясните мне кто-то что такое промышленное приложение? Мне вот сразу представляется код управляющий промышленными станками.
Спасибо. Сейчас впервые пишу приложение на Angular и получаю огромное удовольствие. Но их парадигма затягивает так сильно и так быстро вытесняет привычный jQuery подход, что становится страшно. Самая большая радость от того, что: приложение летает, путаницы в коде нет, всё структурировано, REST интерфейс в сочетании с Node.js и отсутствие необходимости парсить JSON, встроенная помощь в тестировании контроллеров, директивы, которые можно использовать многократно и постепенно улучшать. Вся информация перед глазами пользователя всегда свежая.

Чего только стоит: присваиваешь переменной новое значение и везде где оно используется на экране, оно обновится. Я сначала пытался вспомнить, где я это уже видел много лет назад, оказалось Excel уже давно так умеет)

Удивляюсь, что никто раньше не кричал во весь голос:
— Эй! Ребята, пишущие при помощи jQuery, изучите современные фреймворки и серверные технологии, начните пользоваться SCSS, где можно спокойно использовать переменные и вложенные стили. Изучите три-четыре команды контроля версий Git, почитайте что такое grunt, научитесь применять стили CSS без кнопки F5 (Live reload), научитесь пользоваться Developer Tools в хроме. Научитесь писать тесты.
— Это всё сложно! Мы лучше по старинке.
— Вы так потратите гораздо больше времени и всё равно придёте, но кол-во практического опыта у вас к тому времени станет меньше.

Обожаю современное состояние инструментов программиста. То ли ещё будет…
И ещё испытываю радость от нарушений запретов. Раньше нельзя было использовать глобальные переменные и это считалось плохо. А в Angular ты спокойно объявляешь $scope.peremennaya и можешь её использовать в любых модулях, директивах и контроллерах. Можно даже отправлять сообщения при помощи этой волшебной $scope:
$scope.$emit("Hello, start bomb please","50000 kilotonn");


А в другом месте можно слушать сообщения:
$scope.$on('Hello, start bomb please', function(event, data) { jsStartBomb(data); });


PS: Извините, что увлёкся брызжанием слюной. Замолкаю.
Насчет глобальных переменных — разумеется, не нужно засорять глобальную область, вместо этого используйте модули и загрузчик :)

P.S. Это не противоречит использованию scopes (да и modules) в Angular.
Не совсем понятен пример. Если заменить $scope, на ссылку на объект типа EventManager, то код будет точно таким же и это будет вполне чистый код, без всяких глобальных переменных.
Кстати, разных $scope может быть сколько угодно в один момент времени. Всё зависит от того, как они наследуются, что уже противоречит свойствам глобальных переменных.
Вы немножко неверно понимаете механизм scope. Рассматривайте это скорее, как область видимости, в рамках которой присутствие той или иной переменной оправдано.
Простите за занудство, но jQuery – это не фреймворк, это библиотека.

И каким образом связаны использование того или иного JS-фреймворка\библиотеки с CSS-препроцессорами, сборщиками проектов и инструментами для отладки?
Вы, наверное, профессионал и вам, возможно, трудно понять радости любителя. Восхищение, что когда ты раньше писал лапше код одним способом и при помощи jQuery, а теперь делаешь это там, где твои ребячества не проходят и твоя лапша автоматически группируется в удобные брикеты. Я не к тому, что это всё связано, а к тому, что изучение фреймворков приводит к профессиональному развитию любителей.
Это всё хорошо, просто если совсем расслабиться, то в случае необходимости будет проблемно написать что-то не на «jQuery» или «Angular», а просто на JS.
Не видел этого топика раньше. Спасибо! )))
Ну понятно же что имеется ввиду не jQuery, а 'ручное' DOM-манипулирование, кончено после этого MVVM-магия очень доставляет :)
Попробуйте с помощью Angular и ng-repeat отобразить и заbindить структуру с вложенными коллекциями. В каждом элементе самой глубокой коллекции требуется изменение одного поля, на сервер требуется отправить изменения, структурированные так же, как исходная структура. Для представления требуется также перегруппировать вложенные коллекции по сравнению с исходной структурой. Изменить/упростить представление — не предлагать. Требования реальные: инструментарий локализации интерфейса пользователя. Вложенные коллекции: для каждой локализуемой строки, для каждого языка, между которыми осуществляется перевод, строка, которую нужно отредактировать.

Мой опыт на сравнительно небольшом объеме данных показал время компиляции директив более 2-4 секунд зависания браузера, причем это уже на перегруппированной для представления структуре, которую как-то нужно возвращать в исходный вид.

В результате свой «велосипед» без binding'а и repeat'ов. Из Angular позаимствовал обработку ввода в текстовое поле. Летает.

Вывод: Angular крут для маленьких формочек. Когда дело доходит до коллекций, он меркнет. Будем надеяться, что переход на Object.observe, который идет в отдельной ветке, хоть как-то поможет в runtime (на инициализации при компиляции директив не спасет все равно — DOM штука медленная).

P.S. Ember разочаровывает Handlebars'ом и Metamorph'ом.
Может быть проблема в организации самой структуры? Я подобные структуры перед отправкой из «деревянного» вида преобразую в линейный с parent_id и, соответственно, всё отлично ложится на идеологию ресурсов и на сервер отправляется только то, что изменилось.
С отправкой проблем нет, сервер принимает JSON со структурированными изменениями. Проблема с binding'ом на такую структуру с декларативным построением интерфейса пользователя (ng-repeat + другие директивы).
Как решали? Поделитесь. Сейчас стоит задача создать древовидный контрол (4 уровня). Проблема в том, что элементов на послденем уровне овер 80к. Сделал подгрузку 2х последних уровней с сервера, но даже перывые 2 уровня порядка 150-200 элементов рендерятся с задержкой.
После этого оставил тоьлко 1 уровень порядка 20 элементов, второй вставляется в дом только при раскрытии. Стало хорошо, но еще нужно навешать фильтров и прочих счетчиков, а это добавит еще кучу проверок на каждый элемент. Все делал через ng-repeat рекурсивно
Никак:
В результате свой «велосипед» без binding'а и repeat'ов. Из Angular позаимствовал обработку ввода в текстовое поле. Летает.

Это был эксперимент с Angular после прочтения его исходников и вдохновения его крутостью. Такой же эксперимент хотел провести с Ember после аналогичных действий, но уже не осталось времени — нужно было отдавать инструментарий переводчику.
Свой велосипед без биндингов и репитов был написан давно с использованием jQuery и там больше 1000 строк. Пока 70% этого функционала — 80 строк в директиве. Все это ради желания получить читамый и поддерживамый код.
Велосипед состоит из следующих деталей. Разметка генерируется на сервере, 125 строк PHP. Интерфейс редактирования — это 642 строки JS (в несжатом виде), которые включают:
— класс-модель, обрабатывающую изменения данных (включая моментальное сохранение в jStorage и отложенную отправку на сервер, не блокирующую редактирование);
— класс-представление, обрабатывающее ввод данных и передающее его в модель, а также отображающее наличие несохраненных изменений — использует jQuery для работы с DOM;
— подробные комментарии с jsdoc'ом к методам.
Для обхода структуры пришлось написать метод, содержащий 6 вложенных for'ов.

На Angular получилось 44 строки HTML-разметки + 170 строк JS, в которых определяется несколько директив для обработки логики. Да, меньше в 3 раза, но там еще нет никакой отправки на сервер, контроля изменений и проч., то есть приложение по сути еще ничего не делает — я бросил на том, что рендеринг завис намертво на несколько секунд.

Писать веб-приложение, используя jQuery — не значит писать бешеные цепочки jQuery(selector).click(callback).find(selector).end().fadeIn(time) и подобные, которые подходят разве что для добавления простой интерактивности в веб-сайты.
Ну тут нужно понимать, что при необходимости вставки в DOM огромного (тысячи и более) количества элементов — это не решается никаким «фреймворком». Тут нет какой-то серебряной пули, которая позволит все бац — и сделать сверхбыстрым. Всегда есть какой-то лимит количества элементов, после которого все начнет тормозить. Можно только оптимизировать на уровне архитектуры (загрузка кусками, lazy-loading, fetch-on-scroll, и так далее). Если я правильно понял проблему, конечно.
Тормоза образовались из-за того, что Angular не только вставляет в DOM, но и гуляет по тегам и атрибутам в поисках директив. Это очень круто, если директив немного, а вложенность DOM-дерева небольшая (например, для небольших приложений или для приложений, где в один момент времени идет работа с одним ресурсом). Но в случае с `ng-repeat`, внутри которого есть еще куча директив, на время их сбора наступает зависание.
А, если речь про директивы — да, тут только workarounds. Впрочем, в любом же фреймворке приходится отступать от основной дорожки и придумывать что-то, чтобы не плодить костыли.
Как решали? Поделитесь. Сейчас стоит задача создать древовидный контрол (4 уровня).

Покажу цифры из моего проекта, древовидный список динамических форм (больше среднего размера), если развернуть все объекты и отключить оптимизацию то статистика будет примерно такая: DOM-элементов > 700k, watch'ей > 300k, более 150k директив. Но пользователь все не разворачивает — значит он многое не видит, поэтому нет необходимости обрабатывать большинство элементов прямо сейчас, и мы их отключаем от обработки директивами ng-if, ng-switch и т.п. Плюс ко всему по максимому используется статический биндинг (bind once) где это возможно (bo-if, bo-switch, статические шаблоны {{variable}}). В итоге остается 0,5-1% — что видит пользователь в момент инициализации, этот список сейчас строится примерно за пол секунды на моем буке в crome, в firefox чуть быстрее. Дальнейшая работа пользователя — разворачивание объектов, выполнение команд идет моментально.
Строковой сборкой и пересборкой HTML (с Metamorph всё сложнее, там еще и добавляется пара тегов script вокруг каждой из меняющихся областей, которые фреймворк находит и меняет текст между ними при обновлении модели).

Возможно, я еще не до конца разобрался (только прочитал исходники, не было времени провести тот же эксперимент, который я провел с Angular). Но в приложении, для которого я рассматриваю переход на один из современных фреймворков с data-binding'ом, есть достаточно нетривиальные вомпоненты, которые выходят за рамки работы с текстовыми данными (например, Flash-объект с ExternalInterface'ом, canvas, video). Эти компоненты невозможно пересоздать из HTML-кода в том же состоянии, в котором фреймворк обнаружил изменение модели. Более того, с Flash-объектом вообще следует поступать осторожно и нельзя удалять и пересоздавать, когда вздумается, поскольку это сторонний плагин, и взаимодействие с ним в некоторых браузерах (апчх-ие) происходит асинхронно.
Насчет Handlebars — строковая сборка и пересборка — это проблема именно HB или вообще шаблонизаторов в целом имеется в виду? Так то, вроде, никаких проблем при использовании его с Backbone/Marionette (или Thorax)/Rivets — один раз компилируется и используется дальше.
Это проблема строковых шаблонизаторов. Да, оно-то компилируется в функцию, но функция всё равно возвращает строку, которая потом целиком идет в innerHTML. Если в этой строке оказался, например, canvas, то нарисованное на нём с помощью Canvas API изображение при пере-рендеринге шаблона пропадёт.

Или я чего-то не понимаю?
Нет, нет, все верно, я просто уточняю. Строковые шаблонизаторы более чем имеют право на жизнь, просто не нужно их использовать для всего и вся. Если есть внедренные объекты canvas, video, swf, итд — тут нужен другой подход, очевидно. В том числе, параллельно использовать Handlebars для одних элементов, но canvas отрисовывать другими средствами. canvas ведь сам по себе держит некоторое состояние, которое может держать, к примеру, какая-нибудь модель, а canvas может быть только «дисплеем». Но тут тоже надо осторожно подходить к задаче — ререндеринг всего canvas-а может быть как оправдан, так и нет. Но этот вопрос далеко за пределами «юрисдикции» Handlebars.
Решающим минусом для меня стало, насколько сложно сделать собственную директиву (directive)

Да вроде всё достаточно просто, не? Простые штуки пишутся не намного сложнее чем с jq (ну разумеется при отталкивании от «Angular way»). Да и при сложных компонентах, при усвоении нескольких правил, всё тоже не так пугающе.

И декларативный подход, лично я, не считаю фатальным минусом angular. В действительности, это дает гораздо больше понимания, при беглом осмотре, какую роль играет тот или иной кусок разметки, и даже как себя поведет, и даже кто за это поведение отвечает. Да, on-click, ну дык и что? =) По сути — имеющее право на жизнь удобство =)
См. мой комментарий выше. Хотелось именно декларативности, с преобразованием из одной структуры в другую и автосохранением. Пришлось делать по-старинке.
Марионетка очень понравилась, правда порог вхождения высоковат, сам пару дней просидел тыкав до просветления. Для старта приложения очень рекомендую посмотреть на заготовку https://github.com/juanghurtado/puppeteer
Не нашёл там JS, к сожалению. Все исходники на CoffeeScript :(
С удовольствием использую CoffeeScript для проектов на php и ruby on rails.
Sign up to leave a comment.

Articles