Как стать автором
Обновить

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

И опять, и снова. Боевым гопаком по граблям.

«Я совершенно уверен, что языку JavaScript не хватает трех вещей:
1) Нового фреймворка
2) Новой библиотеки, реализующей „классовую“ ООП
3) Библиотеки UI-виджетов»

P.S. Если серьезно, то у меня не хватает моральных сил даже начать тезисное описание откуда и докуда мне не нравится jWidget.
P.P.S. Angular.js, или там, Meteor.js, мне нравятся не намного больше. Но все же больше.
Вообще, конечно, здорово, мы все любим (кто знает) SOLID и паттерны, и большая работа проделана, и классический ООП-подход прост для понимания, но…

Чем вам так декларативность не угодила, интересно, ведь ее достоинства хорошо известны? Вот вы от нее отказываетесь, и получается куча тупого императивного кода:
var value = new JW.Property(1000);
var unit = new JW.Property("MW");
var functor = new JW.Functor([ value, unit ], function(value, unit) {
    return value + " " + unit;
}, this);
var target = functor.target;

Серьезно, 4 переменных?

И это вот
я много программирую на строго-типизированных языках программирования <...> Поэтому я большой фанат объектно-ориентированного программирования
Что ж, по-вашему, Haskell не строго типизированный?
Я не программирую на Haskell. К чему вопрос?

3 переменных всяко нужны: 2 исходных свойства и одно целевое. В AngularJS такие переменные кладутся в $scope. В EmberJS такие же переменные создаются неявно, и вы имеете к ним доступ через методы get и set. Четвертая переменная — functor — введена для читаемости кода. Как вы видите, в следующих примерах для функторов/мапперов переменные не создаются — они агрегируются, а используется только их target.

Я ничего не имею против декларативности. Если вам нравится программировать через шаблоны — пожалуйста. А кому-то другому больше нравится писать императивный код. В частности, императивный код хорош своим однообразием, подконтрольностью и расширяемостью.
Я не программирую на Haskell. К чему вопрос?
Не путайте парадигму (ООП) и типизацию, вот к чему.

А что такое «программировать через шаблоны»? Если вы про Angular-овские директивы в HTML — так это не единственный метод декларативно объявить биндинги. Да и незачем засорять код тривиальными деталями, в какой инпут какое поле модели пишется. Прекрасный пример декларативности в общем смысле — Backbone.View#delegateEvents.
Да, я неверно назвал объекно-ориентированность строгой типизацией. Спасибо.

В данном примере delegateEvents — это своего рода сахар, который избавляет вас от необходимости для каждого элемента писать this.$el.on("dblclick", this.open.bind(this)). Это нельзя назвать ключевой возможностью фреймворка. Я старался не перегружать свой фреймворк сахаром, т.к. это его усложнило бы. И для привязки обработчиков событий к элементам, и для изменения атрибутов, и для всех остальных операций с элементами предлагается использовать jQuery API, получая элементы через getElement или render[ChildId]. Напишите свою утилитарную функцию delegateEvents, если вам нравится объявлять события, как в Backbone — это несложно.

А вот шаблоны — это ключевая особенность фреймворков AngularJS и EmberJS. Весь функционал крутится вокруг них.

И то, что в Backbone при изменении модели весь View перерендерится с нуля — это тоже ключевой момент фреймворка. Прежде всего, именно по этой причине я в прошлом отказался использовать Backbone.
delegateEvents — это своего рода сахар
любое декларативное программирование — это своего рода сахар, за которым стоят простыни императивного кода, который все эти красивые объявления наделяет жизнью. Только вот в 2014 году мне уже лень писать свой сахар, если давно есть готовый — проверенный, покрытый тестами, с продуманным API.

в Backbone при изменении модели весь View перерендерится с нуля

Это уж как сделаете:) Прикрутить биндинг к бэкбоновским вьюхам, хоть через HTML-ные атрибуты, хоть через словарь в прототипе вьюхи — не rocket science, таких решений уже с десяток, наверное…
То, что я посчитал нужным, я сократил и вынес в методы или классы. delegateEvents я выносить нужным не посчитал. Конечно, в примере Backbone все красиво — аж целых 6 строчек удалось вынести из метода render, чтобы сделать их короче аж на 20 символов. Но в реальности обычно каждый компонент слушает 1-2 события. Взгляните на TodoMVC: в поле ввода слушаем keydown по Enter или сабмит формы (как удобнее), в TodoView слушаем dblclick, а все остальное вяжется со свойствами напрямую — в jWidget через Listener'ы, а в Angular и Ember через волшебные HTML-атрибуты.

Если вы начнете хитро биндиться с данными в Backbone, то вы пойдете в обход фреймворка. Это называется костылями. В документации четко написано, что когда модель выбрасывает событие change, представление перерендерится. Даже TodoMVC, где демонстрируются наилучшие (!) практики работы с фреймворком, это работает именно так. Оттого Select all и тормозит.
Но в реальности обычно каждый компонент слушает 1-2 события.
Да, часто так, но TodoMVC — это вырожденный случай, это демо.

вы пойдете в обход фреймворка. Это называется костылями.
не согласен. Благодаря OOP-подходу в Backbone можно многое переопределить, это очень удобно и документация поощряет это делать.

В документации четко написано, что когда модель выбрасывает событие change, представление перерендерится.
А в следующем параграфе написано, что Backbone — это попытка понять, сколько сущностей минимально необходимо, чтобы строить современные приложения. И нигде не запрещено добавлять сущности, если надо.
Минимальность не обозначает тормознутость. Да и мне в 2014 году уже лень что-то переопределять в фреймворке, чтобы исправить его ошибки.
Попробуйте сами.

todomvc.local/architecture-examples/backbone/

Вот код.

function testSpeed(eventType) {
    var time = new Date().getTime();
    var i = 0;

    function tick() {
        var el = document.getElementById("new-todo");
        el.value = "a";
        var e = document.createEvent("Event");
        e.initEvent(eventType, true, true);
        e.keyCode = 13;
        el.dispatchEvent(e);
        if (++i < 500) {
            setTimeout(tick);
        } else {
            console.log((new Date().getTime() - time) + " milliseconds");
        }
    }

    tick();
}
testSpeed("keypress");


У меня в районе 9500 миллисекунд.

jWidget (http://enepomnyaschih.github.io/todomvc/labs/architecture-examples/jwidget/release/) то же самое, только «keydown». 10000 миллисекунд.

Я и не говорил, что в Backbone записи добавляются медленно. Я говорил, что Select all потом отрабатывает 3 секунды, а в jWidget — мгновенно.
Уточнение: запускается в консольке Google Chrome.
Честно говоря, не понятна методика измерения. То есть какой use case рассматривается, что пользователь будет собственноручно вбивать элементы по одному? Я сомневаюсь, что это разумно.
Куда интереснее сколько времени будет генерироваться представление, когда в него добавят сразу 500-1000 записей. Ведь это обычный кейс, списки приходят от сервера (из localStorage) и мы их добавляем в представление.
Так же если приводите результаты измерений, то лучше выводить их табличкой. Да и избегать оценок «мгновенно» и т.п. — нужны цифры. Тем более их можно получить тем же способом, что вы использовали для основного теста.
Про basis.js ничего сказать не могу. В списке на сайте его нет todomvc.com/
Можете добавиться в местный «пузомерный» хабра-тест
Спасибо за ваш комментарий. Он послужил поводом выполнить некоторые оптимизации. Мне удалось обойти Knockout, Angular и atom, но необходимо выполнить ряд тестов перед выкаткой в релиз. Это входило в мои ожидания, что хабра-сообщество укажет мне на ошибки фреймворка. В следующей версии фреймворка все будет исправлено.

Решения basis.js, jQuery, al-fast-list и оба JS нельзя ставить в один ряд с Angular, Knockout, atom и jWidget, поскольку там идут прямые манипуляции с DOM, что противоречит архитектуре MV*. Методы fill, update и clear должны работать только с моделью.
Если интересно, вот промежуточная версия. Пока надежно работает только в Google Chrome http://plnkr.co/edit/6WhCanCz8hnG6a57VLHc
Вот финальная версия: plnkr.co/edit/6WhCanCz8hnG6a57VLHc

Есть ли способ в Plunker'е примержить этот код к вашей версии? Я не нашел сходу.

image
Ох, как лихо вы разделили ;)
Knockout не framework, angular не MV*. Хотя, конечно, зависит от того, что вы в это вкладываете. Если любая обвязка над значением и биндинги — это MV* фреймворк, то ок.

Решения basis.js, jQuery, al-fast-list и оба JS нельзя ставить в один ряд с Angular, Knockout, atom и jWidget, поскольку там идут прямые манипуляции с DOM, что противоречит архитектуре MV*.

Расскажите, где вы увидили прямые манипуляции с DOM в решение basis.js?
basis является каким то там MV* (не заморачиваюсь на эту тему) — есть и model, и своего рода view и controller.

Методы fill, update и clear должны работать только с моделью.

Модель не является обязательной сущностью. Эту задачу успешно может выполнять и контролер, и представление.
В любом случае «пузкомерка» не для того, чтобы разделить решения на категории и не было особых ограничений. Есть конкретная задача, нужно ее решить.
Конечному пользователю все равно на чем написано приложение и какие патерны используются, для него важно «работает» и «не тормозит».
Я не говорил, что Knockout и Angular — не MV* фреймворки. У меня к тому, что обведено рамкой, претензий нет.

Расскажите, где вы увидили прямые манипуляции с DOM в решение basis.js?


Объявляем basis.ui.Node и в методах fill, update, clear напрямую вызываем его методы setChildNodes, childNodes.forEach, updateBind, clear, которые выполняют DOM-манипуляции. Это точно представление, а не модель. Насколько мне известно, MV* архитектура предполагает, что вы меняете только модель, а представление само при этом понимает, когда и что нужно перерендерить.

Модель не является обязательной сущностью. Эту задачу успешно может выполнять и контролер, и представление.
В любом случае «пузкомерка» не для того, чтобы разделить решения на категории и не было особых ограничений. Есть конкретная задача, нужно ее решить.


Если бы решения Angular, Knockout и jWidget тоже работали напрямую с представлением, то, естественно, они работали бы гораздо быстрее. Я профилировал решение jWidget и обнаружил, что 50% времени съедают накладные расходы на работу с событиями модели.

Конечному пользователю все равно на чем написано приложение и какие патерны используются, для него важно «работает» и «не тормозит».


А еще ему важно, чтобы приложение было легко расширяемым и код был понятным. Именно для этого придумали паттерн MVC. Если везде работать напрямую с DOM, то вообще никакие фреймворки не нужны, но код быстро станет слишком запутанным.
У меня к тому, что обведено рамкой, претензий нет.

Значит ли это, что к basis.js претензии есть? ;)

Объявляем basis.ui.Node и в методах fill, update, clear напрямую вызываем его методы setChildNodes, childNodes.forEach, updateBind, clear, которые выполняют DOM-манипуляции. Это точно представление, а не модель.

Это в большей степени контролер, который абстрагирован от DOM, и ему совсем не важно что там в шаблоне. Он поставляет значения экземпляру шаблона, а шаблон (который может считаться настоящим представлением) уже трансформирует значения в DOM операции.
Так то все приводит к DOM-манипуляциям. В angular делается вызов scope.$scan, в knockout это обновление observables. Не понятно почему в их случае все ок, а в решении basis.js что-то не так.

Насколько мне известно, MV* архитектура предполагает, что вы меняете только модель, а представление само при этом понимает, когда и что нужно перерендерить.

У MV* достаточно много трактовок, и каждый понимает по своему.
Да и представление в basis.js (шаблон) тоже само определяет, что, где и как нужно обновить. Что касается basis.ui.Node – то это абстракция, для организации интерфейса, и имеет косвенное отношение к нативному DOM. Чтобы не придумывать новое за основу была взята модель DOM.
На самом деле basis.ui.Node может являться и контролером, и моделью, и даже представлением при желании. Все зависит от того, что нужно сделать и чего добиться.

Если бы решения Angular, Knockout и jWidget тоже работали напрямую с представлением, то, естественно, они работали бы гораздо быстрее.

Это не так. В том же Angular нет моделей и нет оверхеда по их созданию. Зато есть оверхед на dirty check – и это основной тормоз. В knockout тоже нет моделей и очень дорогая организация структуры ovservables, которая является основной проблемой – а еще неэффективная работа с DOM (с которым как вам кажется он не работает). То есть это все в большей степени из-за архитектурные проблемы.

Я профилировал решение jWidget и обнаружил, что 50% времени съедают накладные расходы на работу с событиями модели.

Работа с моделями не должна съедать столько времени. Посмотрите мой доклад про данные – www.slideshare.net/basisjs/ss-32305540
В дополнение – вот вам решение на basis.js «через модели» и даже с коллекцией. Закономерно, что время увеличилось, но не в разы (на 20-25%): plnkr.co/edit/ZUWORGrGbtOsPJBgTik3?p=preview

А еще ему важно, чтобы приложение было легко расширяемым и код был понятным. Именно для этого придумали паттерн MVC. Если везде работать напрямую с DOM, то вообще никакие фреймворки не нужны, но код быстро станет слишком запутанным.

В вашем случае как раз есть проблема с понятностью, много кода и он запутан. MVC не серебренная пуля. А про DOM я уже написал, кажется вы не до конца разобрались ;)
К basis.js претензий нет :) Есть претензии к решению данной задачи на basis.js. Оно не соответствует архитектуре MV* в привычном смысле.

Один вызов $scan в конце обработчика события допустим, это не сложно. А вот явно создавать Nodes довольно тяжело. А если у вашей модели 10 представлений одновременно, все вручную обновлять будете?

Я ценю ваши достижения в области генерации DOM, но, пожалуйста, не подменяйте понятия. Во-первых, большинство фреймворков не разделяют понятия DOM и представления. Во-вторых, по архитектуре MV* обработка действий пользователя всегда осуществляется через модель, без обращения к представлению. В AngularJS это $scope, в ExtJS это Ext.data.Store, в Knockout это observables, в Backbone это Backbone.Model и Backbone.Collection. Ключевое преимущество, которое дает пользователю архитектура MV* — это возможность заводить несколько представлений для одной модели, возможность подменять представления и возможность менять данные налету, не задумываясь, какие у вас в данный момент существуют представления и в каком они состоянии. Пожалуйста, не путайте людей. Ваш фреймворк basis.js клевый, но это не MV* фреймворк в том виде, в котором он описан в вашей статье и продемонстрирован в «пузомерке». Еще я хотел бы посмотреть реализацию TodoMVC на basis.js.

MV*, конечно, не серебрянная пуля. Вы получаете лучшее качество кода, но теряете производительность. Принимать решение об использовании MV* подхода надо исходя из требований к динамичности приложения и максимальным нагрузкам.
По-моему вы не разобрались, получили неверное представление и делаете поспешные выводы. Но дело ваше.
Реализацию TodoMVC можно найти в demo: github.com/basisjs/basisjs/tree/master/demo/apps/todomvc/basis
Уважаемый, bakhirev.
Это, конечно, здорово, что вы взяли табличку из моей презентации. Но она не для того, чтобы гнуть пальцы или принижать чьи-то других, а для того, чтобы показать как быстро справляются с задачей те или иные решения. Методика измерений совершенно разная, и в данном случае нельзя сопоставлять цифры.
Судя по написанному в статье, работа проделана не малая. Уважайте других, пинать и критиковать проще, чем сделать что-то полезное.
Честно говоря, всё время, пока я читал вашу статью и смотрел примеры кода — я страдал.
Обилие кода для описания простых вещей мне почему-то до боли напомнило YUI3.
Лично моё мнение — написание большого приложения на jWidget принесло бы мне еще больше боли и страдания :)
Направление мыслей хорошее. Но получается слишком много кода и далеко не js-way. Особенно много по созданию классов и настройке рендеринга.
2. Скорость работы скрипта превыше всего.

4. Фреймворк работает на базе jQuery.

Тут определенно что-то лишнее. Если цель добиться высокой производительности, нужно забыть про jQuery, селекторы, html и render. Посмотри в сторону dom-based шаблонизаторов. Сейчас этой дорогой идут basis.js, react.js, ractive и даже meteor. В ember тоже идут в эту сторону, создавая HTMLBars. Вот пара моих докладов по этому поводу: раз и два.

Вообще советую посмотреть basis.js, в нем найдешь много знакомого. Например, те же именованные дочерние представления (сателлиты), или некоторые вещи из работы с данными и др. Только это все более развито…
Если не читал, вот первая часть руководства по фреймворку.
Но получается слишком много кода и далеко не js-way.

Согласен, на типичный JS не похоже. Но объектно-ориентированный подход легко можно будет перенести, скажем, в Dart. Это входит в мои планы.
Если цель добиться высокой производительности, нужно забыть про jQuery, селекторы, html и render.

Тоже согласен. jQuery тормозной. Чтобы фреймворк работал быстро, я использую нативные браузерные манипуляции с DOM в ядре фреймворка, где это возможно. jQuery-обертки над элементами создаются только на финальном этапе, перед тем, как отдать их пользователю фреймворка. Все-таки jQuery на данный момент является самой известной и широко используемой библиотекой для работы с DOM.

Спасибо за ссылки, почитаю.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории