Обновить
Комментарии 41
Простите, но есть куча вспомогательных либ для AngularJS, чтобы сделать его реактивным (FRP) можно написать обертки на watcher, что дает возможность писать в FRP стиле. Вот одна из таких наработок github.com/lauripiispanen/angular-bacon и hueypetersen.com/posts/2013/06/26/helper-functions-for-rx-and-angular. Под промисы тоже много чего есть. Инфраструктура AngularJS очень большая, тем более сейчас разрабатывается такой модуль как github.com/jeffbcross/omnibinder.
Ваш подход очень похож на Javelin.
Я посмотрел на Javelin (https://github.com/tailrecursion/javelin). На первый взгляд он, так же как и RxJs и Bacon, не предоставляет ничего для постройки интерфейсов, только только дает возможность подцепиться к уже существующим элементам.

Примеры на github.com/tailrecursion/javelin-demos тоже все в стиле: вот у нас DOM и мы к нему цепляемся и изменяем. По мне нет смысла городить FRP если конечным шагом является подписка на реактивную переменную и модификация DOM с помощью jquery из обработчика.

Посмотрите на warp9 повнимательнее тут вся соль в композиции (собираем интерфейс по кусочкам) и модульности (любой кусок, вплоть до всего приложения включая view можно завернуть в AMD и загружать через requirejs). Дополнительным бонусом идет подход для сокращения утечек — посмотрите примеры, например, knockout и reactivecoffee этим страдают.
О, тогда Warp9 действительно похож на Javelin+Hoplon. Правда сложность инструментария меня убивает — нужно поставить Java, Clojure, Leiningen, Make, Boot, Hoplon, Javelin и выучить clojure чтобы написать hello world. Как-то уже не хочется проверять его на текучесть, но, подозреваю, что он тоже течет.
Мне тяжело сказать, течëт ли он. В целом вполне возможно, ссылки на слушателей событий хранятся просто в свойстве watches у каждой ячейки.

На тему сложности — что с boot'ом, мне непонятно (в смысле почему они не используют просто лейнинген), но в целом всë проще. Нужна джава и шелл-скрипт для лейнингена, и практически все кложурные/кложурскриптовые проекты поедут. В случае с хоплоном еще нужен boot, но он ставится относительно проще. Clojure, Hoplon, Javelin — это всë библиотеки и они скачаются автоматически, так что на самом деле гемора не так и много.
Очень тяжело читать, куча слипшихся слов «немногосодержательных», «целикомлежит » и т.д.
Парсер хабра, постарался пофиксить.
спасибо, гараздо приятнее было дочитывать)
Относительно сравнения с React:

  • headless — React позволяет ренерить UI на сервере. Он использует Virtual DOM вместо браузерного DOM для построения UI, из-за этого компоненты можно рендерить не только в бразуере, но и внутри Node.js (или любого другого js-рантайма) и Web Worker'а и для этого даже не потребуется бажный и медленный jsdom. Если интересно — можно посмотреть на react-app, набор утилит для рендеринга полностраничных (которые рендерятся прямо в document.documentElement) компонент c помощью React.
  • 2-way bindings — реализуется поверх в десяток строчек кода, но и этого не нужно делать, так как есть уже готовая утилита — ReactLink. Вообще, 2-way bindings имеют довольного ограничинное применение, формочки в основном, поэтому не думаю, что это очень важный пункт сравнения.
  • rich reactive list — абсолютно не важно для React, так как он работает с Plain Old JavaScript Objects, да-да, не стоит заморачиваться и моделировать данные с которыми вы работаете в каких-то observables и прочим.
  • template based — для React есть JSX, который позволяет описывать React компоненты привычным HTML синтаксисом, который имеют очень простую семантику компиляции, например <div>Hello</div> превращается в React.DOM.div(null, 'Hello')
Да, React очень интересный, но мне он показался очень подробным, что-ли, например, в todomvc на нем в 3 раза (!) больше кода, чем в Warp9.

Кроме того, у идея работать с POJSO есть свои минусы — если мы захотим по рабочему приложению получить слепок данных, чтобы их сохранить, например, — нам понадобится в каждый компонент добавить метод для экстракции этих данных из state. Посмотрите на исходники TodoMVC, там, чтобы этого избежать, пришлось все изменения делать внутри одного компонента (TodoApp), что тоже дурно попахивает, если мы говорим об архитектуре.
Кроме того, у идея работать с POJSO есть свои минусы — если мы захотим по рабочему приложению получить слепок данных, чтобы их сохранить, например, — нам понадобится в каждый компонент добавить метод для экстракции этих данных из state. Посмотрите на исходники TodoMVC, там, чтобы этого избежать, пришлось все изменения делать внутри одного компонента (TodoApp), что тоже дурно попахивает, если мы говорим об архитектуре.


Чуть-чуть по-другому, то что весь state содержится в одном компоненте — это наоборот хорошо, а не плохо. Благодаря этому у нас всего один компонент, который содержит изменяющееся состояние, остальные без состояния и поэтому зависят только от своих аргументов (props в терминологии React).

Почему компоненты без внутреннего состояния это хорошо, я думаю, это понятно. Если нет — могу более подробно объяснить.

Хочу также опередить возможный контраргумент и уточнить, что в данном случае Single Responsibility Principle не нарушается — компонент отвечает только за один кусочек данных — список todo items.

Ну и никто не мешает использовать React со всяким «сахаром» для моделей — FRP штуки, Backbone.Model и прочее.
Проблема в том, что при росте приложения держать все состояние и логику работы с ней в одном компоненте проблематично.
Не понял аргумента вообще. Todo очень маленькое приложение и там раскидывать состояние в разные места я не вижу смысла. А в большом приложении вроде бы всë ок, у меня сверху спускаются какие-то общие данные (про пользователя, и все штуки, которые принадлежат ему), а всякое мелкое состояние для компонентов прямо в них определено. Общие данные сейчас бэкбоновые модели и в целом пока терпимо вроде, лучше, чем во всех остальных случаях было (кроме хоплона, ну и варп я не пробовал еще).
Со спуском данных в компоненты (state) проблем нет, но если мы нашу иерархическую фигню, не только показываем и редактируем, но и сохраняем, то тут нужно писать дополнительный код в компонентах для экстракции данных из state.

Получается, что в небольших приложениях мы можем держать всю модель в state одного компонента (случай TodoMVC), а в больших — придется его распиливать и доставать. В этом нет ничего плохого, просто кода станет еще больше, а так React классный.
А, да, есть такое дело. Ты имеешь в виду, что в реакте нет внешнего состояния, которое можно было бы менять из внутренних компонентов? Да, иногда это несколько геморно, иногда плюс (меньше шансов слишком завязаться на глобальное состояние).
Я не копал глубоко React. А разве проблема держать модели и коллекции в виде конкретных полей state? Причем в плане MVC так даже болле логично, вью есть представление конкретной модели. Т.к. работаем со ссылками, экономим место и скорость. И собирать ничего не нужно, компонент возвращает модель с которой работает, при этом в приложении исчезают моменты когда происходит работа с какими либо промежуточными состояниями, только модели.
Хотел еще добавить, что часто именно модель содержит логику валидации, так что не придеться дублировать этот код. Это очень помогает когда абстракция приложения не подразумивает какие либо невалидные состояния.
Еще хотел спросить, что посоветуете прочитать для лучшего понимания работы монад?
Две вещи:
  1. Если нету монады, то можно создать монаду
  2. Если есть монада, то можно взять то, что внутри неё и создать из этого другую монаду


Теперь подставляем вместо слова «монада» конкретный тип данных, с которым мы работает — Maybe/Option, Promise/Future или что-то там ещё и забываем про монады. :p

P.S. Когда тут уже markdown сделают?
Вы, ваще-то, пробовали его запускать? Там нету чекбоксов, нету информации, сколько осталось задач, нету табов 'All', 'Active', 'Completed', нету удаления всех завершенных задач, нету редактирования и, подозреваю, что нету работы с local storage…
Эта библиотека рассчитана для маленьких веб-приложений, где разработчик является верстальщиком и дизайнером, или нет?
Просто я не понял как быть, если мне верстальщик дает 5-и километровый HTML, который нужно «оживить».
Не совсем. Я, скорее, думал про случаи, когда логика приложения сложнее, чем интерфейс. Возьмем, допустим gmail, врят ли при его разработке отталкивались от верстальщика. Вообщем, это библиотека для приложений посложнее CRUD.

Я, например, создал приложение для конфигурации топологии MongoDB кластера с помощью этой библиотеки, а там куча всяких правил, типа в одной шарде должно быть от 2 до 12 нод, только 7 могут голосовать, лучше намекать пользователю, когда он вручную назначает свойства canVote при числе нод меньше 7ми и так далее.
логика в AngularJS отделена от шаблонов (в шаблонов, мы должны всегда помнить о логике (на лицо сайд эффект), но если так, то принцип разделяй и властвуй не работает, следовательно, композиция не дает нам преимуществ, следовательно ее нет.

Навскидку вижу 2 преимущества отделения логики от шаблонов:
* Разделение труда: над шаблоном работает один человек, над логикой — другой.
* Вы можете взять готовую директиву и подключить к своему шаблону (дизайну), т.е. сможете использовать готовые решения.
Мне кажется, что эти два преимущества друг другу противоречат: при разработке директив активно смешиваются код и разметка, а если так, то где разделение труда?

Кроме того, в сложном приложении трудно разделить логику и шаблон, особенно, когда интерфейс активно меняется из логики, а если так, то зачем создавать себе дополнительные сложности, может есть другой путь?
при разработке директив активно смешиваются код и разметка, а если так, то где разделение труда?

Для директив AngularJS используйте templateUrl для указания пути к шаблону (https://github.com/angular-ui/bootstrap/tree/gh-pages тому пример)

В ваших примерах часто используется стилизация через атрибут style
["div", {"css/margin-left": "10px"},

["input-text", {
	"css/background": hello.isSet().when(false, "red")
}, name],

Как тоже самое сделать, только через классы? а то, что бы обновить дизайн страницы, простой верстальщик уже не подойдет.
По поводу стилизации, да можно, но пока, к сожалению, можно добавить только один класс элементу. Это не фундаментальный недостаток, просто руки не дошли пока — большую часть разработки я боролся с утечками и несогласованностью событий. Пример с input-text можно переписать так:

["input-text", {
    "class": hello.isSet().when(false, "empty")
}, name]
По поводу AngularJS. Нет, ну нельзя сказать, что мы используем templateUrl и проблем нет. Потому что в этом случае мы не имеем права говорить:
Вы можете взять готовую директиву и подключить к своему шаблону (дизайну), т.е. сможете использовать готовые решения.
Разница между подключением одного файла (js) и двух файлов (js и шаблон) огромна. В первом случае мы можем говорить про удобную дистрибьюцию, например, AMD, во втором нет — все делаем вручную. Кроме того, во втором случае перед нами открывается целый класс новых ошибок: что если один файл потеряется, что если файл шаблона я положу не туда куда нужно и так далее…
Боюсь, что будет сложно привязывать к дизайну готовые модули созданные на Warp9.
Придется переписывать модуль с учетом необходимого дизайна, причем не только css, но и разметку html блока.

Подход одного js может сработать…
… в разрезе одного большого проекта, когда разные отделы поддерживают одну структуру блоков, у них один конвеншен, один дизайн и тогда можно перекинуть модуль между отделами, разделами сайта, страницами.
… при написании админок для клиентов или своих сайтов — сделал модуль и подключил к админке (один дизайн, одна разметка)

P.S. Некоторые ругают css фреймворки за то, что сайты созданные с их помощью выглядят одинаково, а тут получается, что по разметке они будут похожи.
Ничего не смешивается, если не делать этого намеренно, про templateUrl уже упомянули, но так же есть следующие факты:
— Можно использовать директивы не только как отдельные элементы, но и как некоторое нагромождение функционала на элемент через restrict: 'A|E|C|M', то бишь мы можем, например, навесить несколько обработчиков клика как-то так:
<button click-watcher another-click-watcher>Click me</button>

— Так же есть transclude, с помощью которого можно городить одну директиву внутри другой, в помощь ко всей этой вкуснятине приходит еще и наследование областей видимости ($scope которые), а так же возможность явно указать зависимость директивы от другой.
Старнный у вас пример утечки для knockout… К нему легко можно придраться, сказав, что

this.handlers = ko.observable(0);

нужно заменить на

this.handlers = ko.computed(function(){
            return this.items().length;
        }, this);

Но вообще интересный аспект, не задумывался. Вот поддерживал бы JS слабые ссылки и проблемы такой не было бы.
В смысле, странный? Я считаю то, что течет — подписки у переменной driver, если я начну считать что-то другое (кол-во элементов в списке), то memory leak от этого никуда не денется.
Ну, не странный, а не очевидный что-ли. Если посторонний человек зайдёт на страничку rystsov.github.io/warp9/pages/competitors/leaks/knockout.html то он не поймёт, не читая эту статью, о чём вообще идёт речь. Там не сделан акцент на то, что в this.driver() хранятся даже удалённые объекты nova.
Ну ведь о том и речь, что он не очевидный. Кажется, что всë ок, а на самом деле…
А что получится в ситуации когда «b» вычисляется из «а», а потом вызвать «set» у «b»?
    var a = new Cell();
    var b = a.lift(function(a) {
        return a+2;
    });
    a.set(1);
    // b == 3

    b.set(10);
    // b == ? a == ?
Имхо, StopDoingWrongStuffException должен быть. :)
Знаю, что так делать нельзя. Интересно было, как на такое отреагирует библиотека.
Я, кажется, в презентации это упоминал — у реактивный переменных, полученных иначе, чем через вызов конструктора, нет методов set/unset (если это Cell) и add/remove (если это List).
Как бы это всё к Dojo прикрутить? А то с виджетами куда приятнее работать чем с чистым домом…
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.