Version 1 of the major frameworks came about later than I would have expected them to, as there was a lot of pent up need to address the missing pieces. When these frameworks finally did arrive on the scene and got to a reasonably stable point, public interest took off like wild fire and adoption rates have gone through the roof since then. It’s not surprising – for me personally using both Angular and React I’ve seen myself being able to build complex front ends in a fraction of the time that it took previously.
Trouble in V1 Paradise
The result is that a number of frameworks have quite a bit of old code that wasn’t designed for the purposes we use them for today. Even beyond that when these frameworks were originally built, the complexity of applications we are actually building today wasn’t imagined in those early days. The end result is that frameworks have been refactored from the outside in, resulting in a number of inconsistencies, overly complex APIs and sometimes funky behavior that requires detailed inside knowledge of how the framework works to work around issues.
Since those early days, framework developers have done an admirable job of patching up the existing frameworks, keep them relatively easy to use as well as providing the features that we as developers need to get our job done. As I mentioned in Part 1, it’s amazing what these frameworks have provided us in terms of a leap over your typical jQuery based application we built before it, both in terms of functionality as well as productivity. For me personally the productivity gains in building front end applications has skyrocketed after starting to use a framework (Angular in particular) because it freed me from having to build my own system level components and try to figure out project structure. It's been a huge win for me.
But… looking over how frameworks are working there are many things that are suboptimal. Again speaking from my Angular particular view there are tons of rough edges. In Angular the built-in routing system is terrible even though UI-Router provides some relief for that it's still a very messy way to handle routing. Directives have to be near the top of the list for awkward APIs with its multi-overloaded structure and naming standards (if you can call it that). There are lots of quirks where binding can go terribly wrong if you bind circular references or when you need to do manual binding at the wrong time in the Digest cycle and you end up in an endless digest loop that eventually pops the execution stack. While these are all known and relatively minor issues that have workarounds, it shows the architectural choices from a bygone time catching up with frameworks that are trying to do so much more than they were originally designed for.
Starting Over with V2 Frameworks
Not only are we dealing with new framework re-designs, at the same time we're also dealing with a bunch of new-ish technologies that are disrupting the way things have been done in the past. Among them are:
- Components not Applications
- Platform Changes
- Mobile Optimizations
I've been watching and playing around with Angular 2, Aurelia and Ember 2 for version 2 frameworks. Out of these three only Ember is shipping a production V2 version today while the others are still in alpha and beta state respectively and not looking like they are going to release anytime soon. This post mostly relates to these three frameworks since that's what I've looked at, but much of the content can be applied more broadly to other frameworks as well.
Components over Applications
One theme that all the frameworks are starting to embrace is that applications should be built out of smaller and simpler building blocks. So rather than building large modules or pages/controllers, the idea is to create self-contained smaller components that can be patched together like legos into a larger document. Components have their root in the fledging Web Components standard that seems to have stalled out due too many problems and inconsistencies, but the overall concept of self contained components is still something that each of these frameworks is paying lip service to. All the framework vendors claim some Web Components functionality but in reality it looks like the concept more than the actual implementation is what counts.
In Angular 2 for example, rather than having controllers and directives there will only be components. A component can take over the functionality of what directives and controllers used to do under a single component type. At a high level both do many of the same things - they both have models and binding code to deal with. Likewise services and factories are simply classes in Angular 2. The goal of much of the new functionality is to simplify and strip functionality down to its most essential features to limit the API surface of frameworks which results in simplification on many levels. Certainly angular 1 had a lot of duplicated concepts and in Angular 2 the object model will be much smaller and more concise which makes it easier to work with.
Besides the object model simplification, a component approach can have a profound impact on how you build applications. Rather than building large and complex controllers, you can break out pages into their individual components where sections of a form, or say a list live in their own components. By isolating each of these components and assigning their own logic and data to them they become easier to develop (less dependencies and smaller code base) as well as more easily testable. Angular, Aurelia and Ember all embrace this component style with nested customer HTML tags that describe the individual components in a page.
But having played with components over controller approach I have to admit I have to really force myself to think this way - it's hard to draw the line and not over fragment applications into a million little shards. There's a fine line between not enough and too much componentization. But the beauty of the model is that it's your choice. You can make your components as atomic or as monolithic as you choose.
Improved Databinding Syntax for Angular and Aurelia
Angular 2.0 is making a big break from Angular 1 with drastic changes to its markup syntax for data binding. In Angular you can bind any attribute, event or property through the markup interface using a funky CSS inspired syntax (# for objects/properties,  for attributes and () for events) for example. While the syntax definitely is 'different', it does provide for a well thought out approach for data and event binding that is much more flexible than the old model. This model reduces the need for custom directives just to handle special bindings for each type of control or control attribute. This feature alone should cut down drastically the amount of directives needed in Angular.
Aurelia also has a beautiful, explicit data binding syntax model that uses descriptive prefixes like bind. or delegate. to actively describe the operations to perform for binding. It's very clear and descriptive although a little verbose. Like Angular Aurelia can bind to anything on an element which makes for more flexibility than V1 frameworks had. I'm a big fan of Aurelias binding and validation model. It's very clean and easy to work with in code.
The changes in these frameworks are very welcome as they provide much more flexibility than what was available in V1 frameworks. It also looks like performance of data binding will be much improved as a result of these changes.
The other major change that's happening in the V2 frameworks is that they are all focused on EcmaScript 6 (ES 2015) and beyond. With that change comes a whole slew of other technologies that are required in order to make it all work because even modern browsers cannot run ES6 natively today.
This means new technologies you have to learn and work with:
- EcmaScript 6 (ES 2015)
- ES6 Module System and Loaders
- Build system technologies
Some of the highlight features of ES6 are:
The built-in module system is probably the most notable feature as it greatly simplifies the alphabet soup of module systems that are in use today. Having a standard module system defined at the language level should - in the future - provide for a more consistent experience across libraries instead of the messy multi-module support most libraries have to support today. ES6's module system doesn't support all the use cases of other module systems (there's no dynamic module loading support for example), so we may still need a supporting module system for that, but at least at the application level the interface will be more consistent.
Another great feature are template strings which can be used to merge string and data content inline. This is great for code based output generation, but also useful for things like components which often need to be fully self contained and not ship with external HTML content. In this new word of components inline HTML may not be uncommon and template strings greatly facilitate embedding data into string content for rendering.
Promises are now included in ES6 as part of the base language which again provides a consistent baseline on which libraries can build. The built-in implementation doesn't support all the features a library like Q provides, but it provides the core implementation. Libraries can build ontop and provide the additional functionality. It's been frustrating to see different promise implementation that handle callbacks and error handling using differing syntax and by having a standard in the language at least we're bound to see standardization of the core syntax. All the new major frameworks are using the base ES6 promises and building extensions on top of it.
Arrow functions are like C# lambdas and make for less verbose function syntax. Not exactly a big feature but I have noticed that it does make for more readable code as it cuts down on verbosity for anonymous functions. Unlike standard anonymous functions, arrow functions also guarantee that the parent scope's .this pointer is captured, rather than the active context's which is important when executing inside of the context of a class.
ES6 and (no) Browser Support
We can all agree that ES6 is nice and has many, many worthwhile enhancements. But the reality is that ES6 is not natively supported by any browser shipping today. Some browsers support some of the features, but no browser supports ES6 fully today. if you take a look at this chart, you'll see a lot of red and the highest feature coverage of any browser is FireFox with 71%. It's actually quite surprising that full support isn't here yet especially given that most browsers (with the exception of Internet Explorer) are now evergreen browsers that are automatically updated with new features and standards and the ES6 standard has been more or less finished for nearly a year (although fully ratified only a few months ago). Yet full native support for ES6 looks like it will be off by quite some time yet. My guess is we won't see a full native implementation until early next year and probably a few more months before even all evergreen browsers will be there.
But even when evergreen browsers become capable to run native ES6, there's still the issue of older browsers that won't go away. Internet Explorer versions before 11 and Edge will never get upgraded, and there are literally a billion old smartphones that have old browsers that also won't upgrade. The shift to ES6 with native browser support will take a long time before you can ensure that native ES code runs the way we expect ES5 code to run today.
ES6 requires Transplilation
There are a number of transpilers available today:
- Typescript (still requires traceur or shim transpiler at the moment)
Say Goodbye to Simplicity
Say what you will about the complexity of the V1 set of frameworks, one thing that is nice about them is you don't need much to get started. You can simply add a script reference to a page and you're off and running. The V2 frameworks are not going to be so easy to get started with.
With the ES6 recommendation of these new frameworks (Angular 2, Aurelia, Ember 2) that simplicity is gone as you *have to* use a build process in order to transpile your code before you can execute it. Theorhetically you don't have to use ES6 as the frameworks pay lip service to supporting ES5, but it's clear that this is not the optimal use case. The new frameworks are designed for ES6 and when you look at the examples you are also not likely to want to use ES5 as it's much more verbose to write code with the same functionality.
To be fair once you understand the basic steps involved this process of setting up a simple build environment won't take very long, but if you are a new developer starting from scratch and staring at the first time tutorials I'm not sure that I would stick with it. My first impression would likely be: "Are you fucking serious? You call this easier?"
Even for seasoned developers comfortable with V1 frameworks there will be a learning curve, at the very least picking up ES6 and new module loading syntax and the new fraemwork syntax, but also to work with a whole new set of tools to build your application.
To help alleviate some of this setup, build and configuration pain the new frameworks come with dedicated CLIs that help manage project creation, build process and running watchers. These tools are geared towards easing the repetitive and tedious tasks that you have to go through especially at project creation. This goes a long way to making the process appear simpler as long as it all works.
But when it doesn't, things can get ugly because at this point you have hundreds of files and packaged dependencies created and very little in the way of hints where things went wrong. I ran into this with both Aurelia and Ember starter projects and in both cases the problem ended up being out of date package references which took a bit of time and searching to correct.
Regardless, tooling is going to be a vital part of this lifestyle if there is any hope of getting the unwashed masses to use this stuff or even try it out for that matter. I think it's going to be an uphill battle to get people weaned off the simplicity of the V1 frameworks and get over the AYFS moment. :-)
There's lots of room to improve here. The V2 frameworks are either in Alpha, Beta or just Released so they'll improve and get more reliable with time. Let's also hope that the build tooling and dependency trees can be whittled down over time to reduce the complexity of what is required just to get an application off the ground.
Another important aspect of V2 frameworks is additional focus on mobile development. This comes mostly in the form of optimizations to make sure that frameworks are optimized for mobile devices to use less power and perform adequately. Existing V1 frameworks are notoriously CPU hungry and a lot of focus is going into V2 versions to optimize performance.
All of the new frameworks are using new approaches to deal with view rendering and data binding performance by overhauling their view and binding engines. The React framework seems to have had a massive effect on other frameworks in spurring better performance for rendering large amounts of data, so much so that some frameworks like Ember are emulating some of the React engines features for their own rendering. React's main selling point is that its blistering fast in rendering data, so much so that it doesn't use traditional bindings but rather just redraws the UI as needed from memory rendered buffers. React's approach is more traditional in that it works like a view engine rather than a data binder with the view engine simply re-rendering the entire view rather than trying to update the DOM's existing state.
Angular and Aurelia on the other use data binding to update existing DOM nodes when data changes. In V2 new approaches are used to detect changes. MutationObservers and support for Object.observe() make it easier to detect changes on DOM elements and classes in a highly performant way and this translates into better overall data binding performance.
Additional mobile development features are geared to the animation features built into the frameworks, that are optimized for mobile deveices as much as possible. I haven't really checked this out, but there's quite a bit of hoopla around this aspect.
Native development is definitely an important aspect. For me personally, the last 4 applications I've built have had major mobile components to them and while I prefer building Web based applications that work well on the Web, 2 of those apps needed to be wrapped up in Cordova to provide the functionality we needed. Native frameworks address this need. To me though it's not so much the development of the applications that is at issue though - it's the infrastructure and deployment/testing process that's the big issue. Building applications that can go easily between a Cordova solution and a native Web application require a bit of tweaking, and the entire process of rigging up and testing Cordova applications still feels very cumbersome. Again I think tooling in this space is going to potentially make this better and as far as it goes Telerik seems to have the right idea with their integrated Web and Visual Studio environments.
Mobile is definitely an interesting space - so much is changing and no matter what you use it seems by the time you get rolling with it something new and better shows up…
Two Steps forward One Step Back
I'm taking my two steps forward cautiously. For production use I'm continuing on with Angular 1.x for the time being - after all the V1 frameworks work today. I'm checking out the new frameworks and building a few simple sample apps with them but that's as far as I'm willing to commit for right now. I get questions from customers regularly asking whether they should wait for V2 of this framework or that, and my advice always is - go with what is available today. Don't wait for the new and shiny. Even if it shipped tomorrow you'd need some time for learning, and it's probably a good idea to wait for the first few point releases and see what issues crop up.
The future starts tomorrow. In the meantime get stuff done today…
Other Posts you might also like