AngularJS | Knockout | React | Elm | RxJS, Bacon | ReactiveCoffee | Warp9 | |
---|---|---|---|---|---|---|---|
template based | yes/no[1] | yes | no | no | no | no | |
controls composability | no[2][3] | no[2] | yes | yes | yes | yes | |
modularization of controls | yes/no[4] | no | yes | maybe | yes | yes | |
2 way binding | yes/no[5] | yes | no | yes | yes | yes | yes |
accidental memory leaks | no | yes | no | ? | ? | yes | no |
diamond shape problem | no | yes | no | ? | ? | yes | no |
observable inconsistent state | no | yes/no[6] | no | ? | ? | yes | no |
todomvc | yes | yes | yes | no | maybe | yes/no[7] | yes |
headless | no | yes | no | no | yes | yes | yes |
pure js | yes | yes | yes[8] | no | yes | no | yes |
mixing markup and logic | yes/no[9] | no | yes | yes | yes | yes | |
simplicity | no | yes | yes | yes | yes | yes | yes |
rich reactive list | no | ? | no | yes |
RxJS and Bacon are great libraries to compose events, but to build UI you need an ability to compose not only events, but a UI primitives too. So you can't pick one and build an UI using this library only (actually you can, but you end with dealing with DOM from event handler). I included them only to highlight difference between reactive UI libraries and reactive libraries "to compose asynchronous and event-based programs".
[2] Angular and Knockout allow to break templates into sub templates, place them to different files and include via provided mechanisms (ng-include, template-binding). But it doesn't imply that angular and knockout support modularization and composability of controls.
Don't forget that a complex UI isn't static - it has a behaviour. From semantic perspective this behaviour is high coupled with view, but Angular and Knockout separate behaviour from templates and place it to comptrollers, so if we do something with templates (like composing) we have to keep in mind the behaviour. Does it look familiar? If it were code we would call it side-effect. No matter the nature but any side-effect kills composability, this is the reason why I can't say Angular and Knockout provide it.
I'm not the only one who notices it, the creators of React write:
“React doesn't use templates... ...React is a library for building composable user interfaces.”
If you are still in doubt, think about why the are so few template engine for creating desktop application if the templates are so great.
Whether it is possible to pack control (component or combination of template and controller) in a form which is appropriate for distribution and easy to attach to an application.
Whether it is possible to bind one reactive variable to another and to use bidirectional binding between control and variable.
Whether it is easy to write code that looks fine but leaks with the library. It happens that for all library for which it is true it is also true that code with leaks looks significantly easy than equivalent code without leaks. That means that if you are using such libraries well they lose its elegance.
I've written simple application using Knockout and ReactiveCoffee to demonstrate how easy a leaks can be achieved: app on Knockout и app on ReactiveCoffee. And of cause the same app on Warp9 without leaks.
Whether it possible to form a dependency graph in such way that a single update to a one reactive variable causes multiple updates to another.
I described that situation a bit more and demonstrate with Knockout and ReactiveCoffee.
Whether it possible to observe an inconsistent state due to the lack of atomic updates. Also this problem is described a bit more on a separate page.
[6] Knockout has a workaround to simulate atomic updates but such workaround violates locality of change and breaks composability. There is an information on the topic.
TodoMVC is a standard 'hello word application' for various web UI libraries, it is rule of etiquette for library's author to provide an TodoMVC among examples.
Whether it is possible to use application on server side for dealing, for example, with events.
Has author assumed that the library would be used basically with javascript language?
Does library suggest to mix markup and logic.
It is a common thought that it isn't good and we should separate them. But such approach introduces more problem then solves when we build complex application.
Since UI is a behaviour and behaviour is described by markup and logic, it should be obvious that markup and logic are highly coupled - when we edit markup we should think about code and when we edit code we should think about markup, especially when we store them separately, sounds like side-effect, right? Another point against the separation is complexity of tools. If we store markup and logic separately and our application is big enough we need ways for composition/modularization of markup and ways for composition/modularization of code - we double the count of entities, which is bad, according to Occam.
There is another way to fight complexity - to extract independent behaviour (mix of markup & logic) to controls (abstraction) and to apply use the composition principle to those base controls to build a new one, more complex up to the needed application. There is an analogy to complexity theory: dividing application into markup and code reduce the complexity by factor of 2 (n -> n/2), but abstraction and composition (divide and conquer) reduce it logarithmically (n -> ln (n)).
It is a interesting to notice that libraries forcing you to mix markup and code provide composition and modularization.
Is the number of entities which the library introduces few?
This is very subjective but I think that are a lot of entities in Angular. By the way the developers of React shares this opinion:
Number of concepts to learn
- React: 2 (everything is a component, some components have state). As your app grows, there's nothing more to learn; just build more modular components.
- AngularJS: 6 (modules, controllers, directives, scopes, templates, linking functions). As your app grows, you'll need to learn more concepts.
Almost all reactive libraries provides primitives for working with reactive variables, such as creation of new reactive variable bound with function to another. In knockout you can do it this way:
var a = ko.observable(0);
var b = ko.compute(function() { return a() + 2 });
In warp9:
var a = new Cell(0);
var b = a.lift(function(a) { return a+2; });
But when we look at lists, its support is limited in Knockout and ReactiveCoffee, if we omit some details we find that in those libraries reactive list is just a reactive variable which contains a list. So if you need to do list aggregation and you want this aggregated value to be a reactive variable, you have to redo whole aggregation on each update. For example, if you make a sum aggregation of list and add n values to it then you end with O(n2/2) computations with Knockout or ReactiveCoffee, but only with O(n) with Warp9. Moreover Knockout or ReactiveCoffee don't provide method to do aggregation over reactive list filled with reactive variables, but Warp9 does and your aggregated value will be automatically updated when a reactive list changes or any its reactive variable changes. Example:
var list = new List([new Cell(0), new Cell(1), new Cell(2)]);
var sum = list.reduce(0, function(a,b) { return a + b; });