7 ms·
On "The Future of JavaScript MVC Frameworks"
- coldcode 13y agoThe most amazing and irritating thing about JS frameworks is that every week you find something better.
- jbeja 13y agoUgh, tell me about it :P. But is more something with a different approach than better most of the time. And others times are things that are already there since the beginning of time but remain overlooked
- _random_ 13y agoPick one every _other_ project. This way you will at least skip the ones that will die in that period.
- Joeri 13y agoAnd then you decide on one to do a really big single page app (100.000 lines of code) and end up stuck on a legacy codebase watching the pretty new frameworks sailing by. (Happened to me on an ExtJS codebase started in 2008.)
- bokchoi 13y agoYep, I'm in the same boat. And good luck upgrading ExtJS 3.x to 4.x. Occasionally minor point releases are painful to upgrade to as well. Good lord.
- lightblade 13y agoHa! We're even having trouble going from 4.0 to 4.1 Not really funny come to think of it :(
- k__ 13y agoI started with 4.0 and switched to 4.1 and 4.2 without any problems. But yes, I heared 3 to 4 should be a PITA
- Cthulhu_ 13y agoLikewise; we started building a BackboneJS app two years ago; in the meantime, I'm rebuilding it in Angular for mobile devices, while the main development is going to port the big app to Angular too. Fun fact: Angular is older than Backbone (first commit to angular was four years ago).
- Narretz 13y agoI am also kind of worried that now angular.js has quite a high adoption, and plugins / modules becoming better and better, the devs will focus on angular 2.0. Not only will it use new core technology, but they'll have less time working on the 1.x branch. With over 1000 open issues, this is not a good sign.
- iSnow 13y agoI think the only viable route is to migrate to Angular 2 - the evolution of JS frameworks is way to dynamic to stick with older versions.
- quaunaut 13y agoI hadn't read the original article, "The Future of JavaScript MVC Frameworks", and the article itself[1] is unreadable for what seems like either font or CSS issues. I made a pastie of the text with links to the text there for those who want to read it too. [1]http://swannodette.github.io/2013/12/17/the-future-of-javascript-mvcs/ http://swannodette.github.io/2013/12/17/the-future-of-javasc... [2]http://pastie.org/8768093 http://pastie.org/8768093
- tomjakubowski 13y agoAre you using Chrome? When I visited the page I had this bug, which I've experienced all too often all over the web since the latest update, where no text was visible on the page until I nudged the window size by a couple pixels.
- cbaleanu 13y agoAlso happens to me when using Chrome. They must have broken something with the latest updates.
- STRML 13y agoThere's been a surprisingly bad number of font bugs in the last few months. It's bizarre; I haven't seen browser bugs this visible in years.
- callahad 13y agoPrevious HN discussion: https://news.ycombinator.com/item?id=6936975 https://news.ycombinator.com/item?id=6936975
- scelerat 13y agoIn OP's conclusions: "the DOM is slow, so use a framework that batches updates to it." It would be nice to see a comparison of React/Om with some less naive approach to handling Backbone events. In my own apps, for instance, I've dealt with rapid batch DOM updates simply by putting a short timeout on the event handler and canceling it if another draw-inducing event arrived in the timeframe (5ms, let's say). Doing that has led to similar order-of-magnitude speedups, and while not as sophisticated as using requestAnimationFrame, has been sufficient and maintainable.
- jbeja 13y agoDo you have a open example to this? I would like to know how is implemented.
- eddieplan9 13y agoHe just described the _.debounce function in underscore.js http://underscorejs.org/#debounce http://underscorejs.org/#debounce
- STRML 13y agoThat's something I've recently added to Backbone.LayoutManager[1], but it's still sitting in a PR while we figure out the 1.0 API. The PR is complete, however, so feel free to use my branch. It does offer very meaningful speedups, especially when data changes rapidly or causes a chain reaction. I have not benchmarked it in comparison to React/Om but it is very fast. The implementation is simple; when render() is called, the render is queued up. Every ~16ms (RAF or setTimeout), any pending renders are executed. If another render() is called within that time frame, the earlier is dropped. So long as you expect an async API and listen only to events/hooks ('beforeRender', 'afterRender'), it works pretty well. Backbone.LayoutManager supports it fully and it is configurable in case you rely on synchronous renders, which can be helpful in tests. 1. https://github.com/tbranyen/backbone.layoutmanager/pull/421 https://github.com/tbranyen/backbone.layoutmanager/pull/421
- tomjakubowski 13y agoOm is an immutable data structure library: a structure is never mutated once created, which means that the contents of the entire tree are captured in the identity of the object root. Or in simpler terms, if you are passed an object that is the same object (by identity) that you saw earlier, you can be assured that it hasn't changed in the meantime. This pattern has a lot of nice properties, but also generates more garbage than mutable objects. Is this true to the extent that it typically matters? I would expect so with sort of naive immutability (copying everything whenever you make a change), but not with the kind of structural sharing that Clojure(script)? persistent data structures use.
- STRML 13y agoYou're right in a sense, it generates more garbage than mutable objects because there is more data available than with mutable objects. In most cases it won't matter. Of course if you're holding a global interface undo queue for the lifetime of your page, and your page changes often, you might run into memory issues. But Clojure's immutable datastructures work as well - or better - than you'd expect.
- haberman 13y agoIt's hard for me to say how much it matters, because that would depend a lot on the efficiency of the GC and how many copies are forced (which is a property of the application's data structures). Even with Clojure's sharing, it is still the case (as I understand it) that you're forced to copy all nodes between the root and any node that actually changed. A mutating approach doesn't have to do any copies; it can just mutate the interior node of the tree. As an example of a degenerate case, imagine that your root has 10,000 children. If I understand correctly, any mutation to any object in the entire graph requires duplicating the root and the entire 10,000-element array. I honestly have no idea if this would be an important issue in practice or not, I just wanted to mention it as a super-brief pro/con list of immutable data structures.
- swannodette 13y agoClojure and ClojureScript data structures use 32 way branching nodes. This means even with >2 billion elements in say a persistent vector you'll never have more than 7 hops on any particularly path. This means in the worst case you will need to copy and update 7 children all which are small arrays. I did a bunch of GC testing with Om, the GC profile wasn't dramatically different than naive Backbone.js on TodoMVC which is also working GC by blowing away the DOM on every update.
- drfloob 13y agoWell, we can check a few of those guesses pretty easily. Here is the stock React TodoMVC demo augmented with Swannodette's benchmarks: http://drfloob.github.io/todomvc/architecture-examples/react/ http://drfloob.github.io/todomvc/architecture-examples/react... Benchmark to your heart's content! On my system, Om is about 4x faster on benchmark 1, and 1000x faster on benchmark 2. I don't think requestAnimationFrame has a lot to do with it, and immutability only slightly more; I think the real performance gain is in having an application data policy tailored to make the most of React's behavior. To plug my own work a bit, I built a TodoMVC example with React and my own immutable data structure library in pure JS. It performs a lot like Om on both benchmarks, and actually seems a bit snappier in places, like toggling and clearing all completed todos: http://drfloob.github.io/todomvc/labs/dependency-examples/react__tree/ http://drfloob.github.io/todomvc/labs/dependency-examples/re...
- haberman 13y agoThanks for posting this; I wished when writing the article that I had plain React benchmarks to run. However when I profiled your benchmark it seems like it is invoking React's event loop more than I would expect (and more than the React/Om benchmark does) -- do you have any idea why this would be? It seems like for this to be apples to apples, it shouldn't be invoking React's update logic (ie. re-rendering everything) until the end of the benchmark. Perhaps this difference is because of requestAnimationFrame? I don't understand this comment: > I don't think requestAnimationFrame has a lot to do with it, and immutability only slightly more; I think the real performance gain is in having an application data policy tailored to make the most of React's behavior. As I mentioned in my article, "Benchmark 2" is a no-op on the DOM. So literally all React should be doing is calling render() before the benchmark (which returns basically nothing), letting the entire benchmark run, then calling render() again (in which it again returns basically nothing). In other words, I don't see what the "application data policy" has to do with making React efficient in this case; all we're asking React to do here is calculate a no-op diff and then do nothing.
- drfloob 13y ago> it seems like it is invoking React's event loop more than I would expect ... Perhaps this difference is because of requestAnimationFrame? I think so, yes. The performance difference is already negligible with Om, so I didn't see a real need to optimize it any further. Pete Hunt's react-raf-batching (mentioned already) could probably be dropped in to get that last bit of optimization, but I haven't tried. As for what I mean by "application data policy", I think that if you work with your application's data in such a way that it batches modifications, renders entirely from the top down, and uses fast `shouldComponentUpdate` implementations, you'd achieve most of the improvement you see in the Om vs React+Backbone TodoMVC comparison. Lots of things could achieve that. Immutability isn't a hard requirement to get those features done. And if my React+_tree example is any indication, requestAnimationFrame isn't buying you that much performance either. I haven't really analyzed what Om's Benchmark 2 was doing under the hood, so thank you for that. I assumed it was doing something, but with the ~4ms benchmark, I just assumed that particular something was wizardry.
- tomphoolery 13y agoIt's kind-of funny to me see an article entitled "the future of javascript" anything that only talks about a ClojureScript framework.
- loz220 13y agoHaving recently worked on optimizing the performance of my own view extension library to Backbone (kettle.js), I can give some explanation about the relatively poor performance of Backbone's todoMVC implementation in benchmark#1 and #2. Benchmark 1) This benchmark deals with adding a bunch of todos and seeing how fast they render. The major performance impact here seems to be jQuery. Specifically jQuery event delegation. I was surprised to find that binding DOM events in jQuery are incredibly slow, in fact the majority of the time in that benchmark is spent event binding. For comparison have a look at exoskeleton, a Backbone fork which removes the jQuery dependency and uses the native DOM methods instead. It's also available on todomvc.com and uses pretty much the same code as the Backbone todo implementation. Given the following benchmark : var benchmark = function() { app.todos.reset(); var s = [];var i=0; while (i<1000) {s.push({title:'foo'});i++}; var t = performance.now(); app.todos.reset(s); return performance.now() - t; } Backbone: (http://todomvc.com/architecture-examples/backbone/ http://todomvc.com/architecture-examples/backbone/) >>benchmark() >>667.2439999965718 Exoskeleton: http://todomvc.com/labs/architecture-examples/exoskeleton/ http://todomvc.com/labs/architecture-examples/exoskeleton/ >>benchmark() >>226.4119999963441 It's worth mentioning that Backbone has a couple pull requests open that is meant to address this. Benchmark 2) This benchmark deals with toggling all of the todos events at once. It builds up 200 todos and toggles them on and off 5 times. The problem lies with using 'all' and 'change' events way too liberally within the todo application. Which causes a ton of needless render calls. During the course of this benchmark the main view has render called on it 6000 times (!) while the child views have render called on them for a total of 3000 times. I'm actually surprised that Backbone is as fast as it is given those type of numbers. What should happen is the main view should only need to render a total of 5 times (once for each toggle) and the child views 1000 times (200 items x 5 times).
- cies 13y agoI've used Backbone, Spine, Angular and some handrolled solutions. It never clicked. But maybe I just don't like JS.. Recently I was trying to use Fay (http://fay-lang.org http://fay-lang.org a proper subset of Haskell that compiles to JS) and I loved it. Now if only I can use it with some sort of FRP library :)
- skrebbel 13y agoIf it compiles to JS, I assume that you can call JS from it? The framework of question in this thread, React, actually meshes really well with an FRP kind of thinking and with immutable data. Read the post Haberman links to.
- swannodette 13y agoI appreciate the skepticism :) But remember the goal of Om is to be simple and fast in more scenarios than are allowed in traditional designs. Emphasis on simple. Take a look at my undo example implemented in about 5 lines of relevant code http://swannodette.github.io/todomvc/labs/architecture-examples/om-undo/index.html http://swannodette.github.io/todomvc/labs/architecture-examp.... Note the render time when undoing changes, much less than 16ms. That's undo playback at 60 frames per second. The ability to snapshot the application state is not a small thing, it's a global application concern with lots of benefits besides user level undo - UI rollback on network request failure, app state restoration sans boilerplate when the user cancels something, simpler model for offline applications, and UIs based on scrubbing come immediately to mind. I'm sure you could achieve similar or better performance in a system that relies on events with batching and mutable objects but I'm not convinced that it can be done as simply as we can do it today in Om.
- skrebbel 13y agoDavid, thanks for your work on Om and, especially, writing about it. Your writing introduced me to React and how to use it with a de-emphasis on state. Having very little FP experience and zero Clojure experience, however, sometimes you're going a little fast for me, so was hoping whether you or someone else who reads this could help me out. I don't completely understand how entirely immutable data can be effectively used in front end apps (I do understand how, if you have immutable data, React can be made fast and undo is easy). If data changes, it needs to mutate, right? Do you then maintain one global object that does change, and have that object be a giant immutable tree with all model data? Or am I completely missing the approach? If that's the way, how can you efficiently do updates on relatively large chunks of data? Say, I'm coding Gmail and my user wants to star the 2053rd email in a list? Doesn't that mean 2052 items in a list have to be copied? In general, doesn't this make model code much much more complicated? Or am I missing something and is it really not that bad? Yeah, a bit many question marks, but I hope you catch my drift :) If anyone could point me to some good reading about thinking in immutable data terms, that would be warmly appreciated too. Thanks!
- swannodette 13y ago