As a portion of our elaborate training events I give a short talk about JS frameworks. I've shied away from posting many of my opinions about frameworks online because it tends to stir the pot, hurt people's feelings, and unlike talking face to face, there's no really great, bi-directional channel for rebuttals.
But, I've been told that it was very useful and helped provide a nice, quick overview of some of the most popular JS tools and frameworks for building single page apps. So, I decided to flesh it out and publish it as A Thing™ but please remember that you're just reading opinions, I'm not telling you what to do and you should do what works for you and your team. Feel free to disagree with me on twitter or even better, write a post explaining your position.
Angular.js
pros
-
Super easy to start. You just drop in a script tag into your document add some
ng-
attributes to your app and you magically get behavior. -
It's well-supported by a core team, many of whom are full time Google employees.
-
Big userbase / community.
cons
-
Picking Angular means you're learning Angular the framework instead of how to solve problems in JavaScript. If I were to encourage our team to build apps using Angular, what happens when {insert hot new JS framework} comes along? Or we discover that for a certain need, Angular can't quite do the thing we want it to and we want to build it with something else? At that point how well will those angular skills translate to something else? Instead, I've got developers who's primary skill is Angular, not necessarily JavaScript.
-
Violates separation of concerns. Call me old school, but I still believe CSS is for style, HTML is for structure, and JavaScript is for app logic. But, in Angular you spend a lot of time describing behavior in HTML instead of JS. For me personally, this is the deal breaker with Angular. I don't want to describe application logic in HTML, it's simply not expressive enough because it's a markup language for structuring documents, not describing application logic. To get around this, Angular has had to create what is arguably another language inside HTML and then also writing a bit of JS to describe additional details. Now, rather than learning how to build applications in JavaScript, you're learning Angular and things seem to have a tendency to get complex. That's why my friend Ari's Angular book is 600 pages!
-
Too much magic. Magic comes at a cost. When you're working with something that's highly abstracted, it becomes a lot more difficult to figure out what's wrong when something goes awry. And of course, when you veer off the beaten path, you're on your own. I could be wrong, but I would guess most Angular users lack enough understanding of the framework itself to really feel confident modifying or debugging Angular itself.
-
Provides very little structure. I'm not sure a canonical way to build a single page app in Angular exists. Don't get me wrong, I think that's fine, there's nothing wrong with non-prescriptive toolkits but it does mean that it's harder to jump into someone else's angular app, or add someone to yours, because styles are likely very different.
my fallible conclusion
There's simply too much logic described in a quasi-language in HTML rather than in JS and it all feels too abstract and too magical.
I'd rather our team get good at JS and DOM instead of learning a high-level abstraction.
Ember.js
pros
-
Heavy emphasis on doing things "The Ember Way" (also note item #1 in the "cons" section). This is a double edged sword. If you have a huge team and expect lots of churn, having rigid structure can be the difference between having a transferrable codebase and every new developer wanting to throw it all away. If they are all Ember devs, they can probably jump in and help on an Ember project.
-
Outsource many of the hard problems of building single page apps to some incredibly smart people who will make a lot of the hard tradeoff decisions for you. (also note item #2 in the "cons" section.)
-
Big, helpful community.
-
Nice docs site.
-
A good amount of existing solved problems and components to use.
cons
-
Heavy emphasis on doing things "The Ember Way". Note this is also in the "pros" section. It's very prescriptive. While you can veer from the standard path from the sound of it, many do not. For example, you don't have to use handlebars with Ember, but I would be surprised if there are many production Ember apps out there that don't.
-
Ember codifies a lot of opinions. If you don't agree with those opinions and decide to replace pieces of functionality with your own, you're still sending all the unused code to the browser. Byte counting isn't a core value of mine, but conceptually it's nicer to be able to only send what you use. In addition, when you're only sending what you're using, there's less code to sift through to locate the bug.
-
Memory usage can be a bit of an issue, which can be a problem, especially when running Ember on mobile
-
Ember is intentionally, and structurally inflexible. Don't believe me? Take Yehuda's word for it instead (the surrounding conversation is interesting too).
my fallible conclusion
The lack of flexibility and feeling like in order to use Ember you have to go all or nothing is a deal breaker for me.
React
It's worth noting that it's not really fair to include React in this list. It's not a framework, it's a view layer. But there's so much discussion on this that I decided to add it here anyway. Arguably, when you mix in Facebook's flux dispatcher stuff, it's more of a framework.
pros
-
Blindly re-render without worrying about DOM thrashing, it will "diff" the virtual DOM that you render to, against what it knows the DOM is and will perform minimal changes to get them in sync.
-
Their virtual DOM also resolves issues with eventing across browsers by abstracting it to a standards-compliant event-emitting/bubbling model. As a result, you get a consistent event model across any browser.
-
It's just a view layer, not a complete framework. This means you can use it with whatever application orchestration you'd like to do. It does seem to pair nicely with Backbone, since Backbone doesn't give you a view binding solution out of the box and encourages you to simply re-render on model changes, which is exactly what React encourages and deals with.
cons
-
The template syntax and the way you create DOM (with JSX) is a bit odd for a JS developer because you put unquoted HTML right into your Javascript as if it were valid to do so. And yes, JSX is optional, but the alternative:
React.DOM.div(null, "Hello ", this.props.name);
isn't much better, IMO. -
If you want really finite and explicit control over how things get applied to the DOM you don't really have it anymore. For example, if you want very specific control over how things are bound to style attributes, for creating touch draggable UIs. You can't easily time the order of how classes get applied, etc. (please note this is something I've assumed would be an issue but have not run into myself, but this was confirmed by a dev I was talking to who was struggling with exactly this. But take it with a grain of salt).
-
While you can just re-render the entire react view, depending on the complexity of component, it sure seems like there can be a lot of diffing to do. I've heard of React devs choosing to update only the known changed components, which to me, takes away from the whole idea of not having to care. Again, note that I'm speaking from very limited experience.
my fallible conclusion
I think React is very cool. If I had to build a single page app that supported old browsers I'd look closely at using Backbone + React.
A note on the "FLUX" architecture: To me this is not new information or even a new idea, just a new name. Apparently I'm not alone in that opinion.
The way I understand it, conceptually FLUX is the same as having an intelligently evented model layer in something like Ampersand or Backbone and turning all user actions and server data updates into changes to that state.
By ensuring that the user actions never result in directly manipulating the DOM you end up with the same unidirectional event propagation flow as FLUX + React. We intentionally didn't include any sort of two-way bindings in Ampersand for that reason. In my opinion two-way bindings are fraught with peril. Having a single layer deal with incoming events, be they from the server or user action is what we've been doing for years.
Polymer
This one is a bit strange to me. There's a standard being developed for being able to define custom elements (document.registerElement
for creating new HTML tags with built in behavior), doing HTML imports (<link type='html'>
for being able to import those custom elements into other documents), and shadow DOM (for isolating CSS from the rest of the document).
Those things are great (except HTML imports, IMO).
But, judging by Polymer's introduction, it sounds like a panacea for making all web development easy and amazing and that it's good for everything. Here's what the opening line says:
Web Components usher in a new era of web development based on encapsulated and interoperable custom elements that extend HTML itself. Built atop these new standards, Polymer makes it easier and faster to create anything from a button to a complete application across desktop, mobile, and beyond.
While I think being able to create custom elements and encapsulating style and behavior is fantastic, I'm frustrated with the way it's being positioned. It sounds like you should use this for everything now.
Here's the kicker: I don't know of any significant Google app that uses polymer for anything.
That's a red flag for me. Please don't misunderstand, obviously this is all new stuff and change takes time. My issue is just that the messaging on the site and from the Google engineers working on this don't convey that new-ness.
In addition, even if you were to create custom elements for all the view code in your single page app, something has to manage the creation/destruction of those elements. You still have to manage state and orchestrate an app, which means your custom elements are really just another way to write the equivalent of a Backbone view. In the single page app world, I don't see what we would actually gain by switching those things to custom elements.
pros
-
Being able to create things like custom form inputs without them being baked into the browser is awesome.
-
Polymer polyfills enough so you can start using and experimenting with this functionality now.
-
Proper isolation of styles when building widgets has been a problem on the web for years. The new standards solve that problem at the browser level, which is awesome.
cons
-
I personally feel like one of Google's main motivations for doing this is to make it dead simple to drop in Google services that include behavior, style and functionality into a web page without having to know any JS. I could be completely off base here, but I can't help but feel like the marketing push is largely a big hype push to help push the standards through.
-
HTML Imports seem like a bad idea to me. It's feels like the CSS @import problem all over again. If you import a thing, you have to wait to get it back before the browser notices that it imports another thing, etc. So if you actually take this fully componentized approach to building a page that is promoted, then you'll end up with a ton of back and forth network requests. They do have a tool called the "vulcanizer" for flattening these things out, however. But inlining it doesn't seem to be an option. There's was a whole post written yesterday about the problems with HTML imports that discusses this and other issues.
-
I simply don't understand why Google is pushing this stuff so hard as if it's some kind of panacea when the only example I can find of Google using it themselves is on the Polymer site itself. The site claims "Polymer makes it easier and faster to create anything from a button to a complete application across desktop, mobile, and beyond." In my experimentation, that simply wasn't the case, I smell hype.
my fallible conclusion
Google doesn't seem to be eating their own dog food here. The document.registerElement
spec is exciting, beyond poly-filling that, I see no use for Polymer, sorry.
Backbone
There is no more broadly production deployed single page app framework than Backbone that I'm aware of. The examples section of the backbone docs lists a lot of big names and that list is far from exhaustive.
pros
-
It's a small and flexible set of well-tested building blocks.
- Models
- Collections
- Views
- Router
-
It solves a lot of the basic problems.
-
Its limited scope makes it easy to understand. As a result I always make new front end developer read the Backbone.js documentation as a first task when they join &yet.
cons
-
It doesn't provide solutions for all the problems you'll encounter. This is why every major user of backbone that I'm aware of has built their own "framework" on top of Backbone's base.
-
Most notably find yourself missing when using plain Backbone are:
- A way to create derived properties on models.
- A way to bind properties and derived properties to views.
- A way to render a collection of views within an element.
- A way to cleanly handle "subviews" and nested layouts, etc.
-
As much as backbone is minimalistic, it's pieces also arguably too coupled to each other. For example, until my merged pull request is released you couldn't use any other type of Model within a Backbone Collection without monkey patching internal methods. This may not matter for some apps, but it does matter if I want to, for example, use a model to store some observable data in a library intended for use by other code that may or not be a backbone app. The only way to use Backbone Models is to include all of Backbone which feels odd and inefficient to me.
my fallible conclusion
Backbone pioneered a lot of amazing things. I've been using it since 0.3 and I strongly agree with its minimalistic philosophy.
It's helped spawn a new generation of applications that treat the browser as a runtime, not just a document rendering engine. But, its narrow scope left people to invent solutions on top of Backbone. While this isn't a bad thing, per sé, it just brings to light that there are more problems to be solved.
Not using a framework
There's a subset of developers who think you shouldn't use frameworks, for anything ever. While I appreciate the sentiment and find myself very in line with many of them generally, to me it's simply not pragmatic, especially in a team scenario.
I tend to agree with Ryan Florence's Post on this topic. Which is best summed up by this one quote from his post:
When you decide to not pick a public framework, you will end up with a framework anyway: your own.
He goes on to say, that doing this is not inherently bad, just that you should be serious about it and maintain it, etc. I highly recommend the post, it's excellent.
pros
-
Ultimate flexibility
-
You'll tend to include only the exact code that you need in your app.
cons
-
Massive re-inventing of things, cost.
-
Knowing what modules to use and finding the right modules is hard
-
No clear documentation or conventions for new developers
-
Really hard to transfer and re-use code for your next project
-
You'll generally end up having learn from your own mistakes instead of benefiting from other's code.
The GIANT gap
In doing our trainings and in writing my book, Human JavaScript and within our team itself we've come to realize there is a huge gap between picking a tool, framework, or library and actually building a complete application.
Not to mention, there are huge problems surrounding how to actually build an app as a team without stomping on each other.
There are sooooo many options and patterns on how to structure, build, and deploy applications beyond just picking a framework.
Few people seem to be talking about how to do all of that, which is just as big of a rabbit hole as picking a framework!
What we actually want
-
Clear starting point
-
A clear, but not enforced, standard way to do things
-
Explicitly clear separation of concerns, so we can mix and match and replace as needed
-
Easy dependency management
-
A way to use existing solutions so we don't have to re-invent everything
-
A development workflow where we can switch from development mode to production with a simple boolean in a config.
How we've addressed all of these things
So, in case you hadn't already heard, we did the unspeakable thing in JavaScript. We made a "new" framework: Ampersand.js It's a bit like a redux or derivation of Backbone.
The response so far, has been overwhelmingly positive, we only announced it about a month ago and all these folks have jumped in to contribute. People have been giving talks about it at meetups, and Jeremy Ashkenas, the creator of Backbone.js, Underscore.js, and CoffeeScript invited me to give a keynote at BackboneConf 2014 about Ampersand.js.
So how did we address all my critiques about the other tools?
-
Flexible but cohesive
-
It comes with a set of "core" modules (documented here) that roughly line up with the components in Backbone. But they are all installed and used individually. No assumptions are made that you're using a RESTful or even Ajax powered API. If you don't want that stuff, you just use Ampersand-State instead of the decorated version of State we call Ampersand-Model that adds the restful methods.
-
It doesn't come with a templating language. Templates can be as simple as a string of HTML, a function that return a string of HTML, or a function that return DOM. The sample app includes some more advanced templating with templatizer, but it truly could be anything. One awesome approach for doing handlebars/htmlbars + Ember style in-template binding declarations is domthing by Philip Roberts. There are also people using React with Ampersand views.
-
Views have a way to declare bindings separate from the template engine. So if you want, you can use HTML strings for templates and still get full control of bindings. The nice thing about not bundling a templating engine means that you can write componentized/reusable views without needing to also include a templating system.
-
-
There has to be a clear starting point and some idiomatic way to structure the app as a whole that can be used as a reference, but those standard approaches should not enforced. We did this by building a CLI that can help you spin up a new app, that follows all these conventions that can serve either as a starting point, or simply as a reference. See the quick start guide for more.
-
We wanted to build on something proven not just start something new for the sake of doing it. This is why we built on Backbone as a base instead of starting from scratch entirely.
-
We wanted a more complete reference guide to fill that gap I mentioned that explains all the surrounding ideas, tools, and philosophies. We did this by writing a book on the topic: Human JavaScript. It's free to read online in its entirety and available as an ebook.
-
We wanted to make it easy to use "solved problems" so we don't have to re-invent the wheel all the time. We did this by using npm for all package management, and by creating a quick-searchable directory of our favorite clientside modules.
-
We wanted a painless development-to-production workflow. We did this with a tool called moonboots that adds some dev and deployment workflow functionality to browserify. Moonboots has a plugin for hapi.js and express.js where the only thing you have to do to go from production mode (minified, cached, uniquely named static assets) and dev mode (re-built on each request, not minified, not cached) is toggling a single boolean.
-
We didn't just want this to be an &yet project, it has to be bigger than that. We've already had over 40 contributors in the short time Ampersand.js has been public, and we just added the first of hopefully many non-&yet contributors to core. Everything uses the very permissivie MIT license and its modular, loosely coupled structure lends itself quite well to extending or replacing any piece of it to fit your needs. For clarity we've also set it up as its own organization on GitHub.
-
We wanted additional training and support to be available if needed. For this we've made the #&yet IRC channel on freenode open to questions and support. In addition there are people and companies who want paid training opportunities to be available in order for them to even feel comortable adopting a technology. They want to know that more information and help is available, so in addition to the free resources, we've also put together a Human JavaScript code-along online training and offer in person training events to provide hands-on training and support.
So are you saying Ampersand is the best choice for everyone?
Nope. Not at all. It certainly has its own set of tradeoffs. Here are some I'm aware of, there are probably others:
-
Unsurprisingly, it is still a somewhat immature codebase compared to some of these other tools. Having said that, however, we use it for all our single page app projects at &yet and the core modules all have thorough test suites. It's also worth noting that if you do run into a problem, odds are it won't be as debilitating. Its open, hackable, pluggable nature makes it different than many frameworks in that you don't have to jump through a bunch of hoops to fix or overwrite something in your app. The small modules typically make it easier to isolate, patch, and quickly publish bugfixes. In fact, we often publish a patched version to npm as soon as a pull request is merged. Our strict adherance to semver makes it possible to do that while mitigating odds of breaking any existing code. I think that's part of the reason it has gotten as many pull requests as it has already. Even still, if you have a different idea of how something should work, it's easy to use your own module instead. We're also trying to increase the number of core committers to make sure the patches are getting in even if other core devs are busy.
-
It doesn't have the rich tooling and giant communities built up around it yet. That stuff takes time, but as I said, we're encouraged by the level of participation we've had thus far. Please file bugs and help create the things you wish existed.
-
Old browser support is a rough spot. We intentionally drew a line saying we won't support IE8. We're not the alone there, jQuery 2.0 doesn't either, Google has said they'll only support the latest two versions of IE for Apps and recently dropped IE9 too, and Microsoft themselves just announced their plan to phase out support for all older browsers. Why did we do this? It's because we're using [getters and setters] for the state management stuff. It was a hard decision but felt like enough of a win to make it worth it. Unfortunately, since that is a language-level feature, It's not easily shimmable (at least not that I'm aware of). Sadly, for some companies not supporting IE8 is a dealbreaker. Perhaps someone has already written a transpiler in a browserify transform that can solve this problem, but I'm not aware of that. If you are, please let me know. I would love it if Ampersand-State could support IE 7 and 8.
Final thoughts
Hopefully this explanation was useful. If you have any feedback, thoughts or if there's something I missed or got wrong I'm @HenrikJoreteg on twitter, please let me know.
Also please help us make these tools better. We love getting more people involved in the project. File bugs or grab one of the open issues and help us patch 'em.
Want to start using Ampersand?
Check the learning guides, API reference, or read Human JavaScript online for free.
For hands-on learning jump into the Human JavaScript code-along online training, or for the ultimate kickstart come hang out in person at our training events where you'll build an app from scratch together with us.
See you on the Interwebz <3