The Evolution of Web Frameworks

ES6 and Beyond

Created by Ben Gillies / @bengillies

The current state of web frameworks

There are literally tens of frameworks in use today

Well, ok hundreds. Of those there are three main frameworks in widespread use

  • Backbone
  • Angular
  • Ember

They're all really different

However, they all have a few common parts, and they all share generally the same set of problems.

  • Routing
  • AJAX/Model serialization and manipulation
  • Rendering and/or data binding

Problems

Clunky APIs

Difficult to learn and/or structure

Hard to debug

Reinventing the wheel

Bulky filesize

Awkard and slow rendering and data binding

ES6 to the rescue

ES6 is the next version of JavaScript (sort of). It's currently under development by TC39 committee at ECMA International and will introduce several new features that are currently impossible under ES5.

Major ES6 features

  • Modules
  • Generators
  • Promises (ES7)
  • Object.observe
  • Weak Maps
  • Template Strings

Modules

module "Post" {
  import { Object: Obj, computed } from Ember;
  import store from App;

  export default Obj.extend({
    comments: computed((id) => store.find('comments', id)).property('id')
  });
}

Object destructuring

let { a, b } = { a: "foo", b: "bar" };

let [ head, ...tail ] = [ 1, 2, 3, 4, 5 ];

let { document: page } = { document: "Lorem Ipsum dolor sit amet" };

function foo({ a, b: [ head, ...tail ] }) {
	// do some stuff
} 

Arrow Functions

let obj = {
    compute: function(val) {
        return this.storedVal * val;
    },
    computifier: function(val) {
        var self = this;
        this.storedVal = val;
        return function(otherVal) {
            return self.compute(otherVal);
        };
    }
} 
let obj = {
    compute(val) {
        return this.storedVal * val;
    },
    computifier(val) {
        this.storedVal = val;
        return (otherVal) => this.compute(otherVal);
    }
}
  • AMD sucks!—clunky API; awkward to use
  • Clean syntax
  • Custom loading behaviour
  • Clear dependencies
  • Don't need global object
  • Works with object destructuring

Generators

function renderPost() {
    var template = this.get('post'),
        self = this;

    store.find('post', this.get('id'), function(post) {
        store.fetch('comments', self.get('id'), function(comments) {
            template.render({
                post: post,
                comments: comments
            });
        });
    });
}
function renderPost() {
    var template = this.get('post');

    RSVP.hash({
        post: store.fetch('post', this.get('id')),
        comments: store.fetch('comments', this.get('id'))
    }).then(function(data) {
        template.render(data);
    });
}
function* renderPost() {
    let template = this.get('commentTemplate');

    let data = yield RSVP.hash({
        post: store.fetch('post', this.get('id')),
        comments: store.fetch('comments', this.get('id'))
    });

    template.render(data);
}
function render() {
    let generator = renderPost();
    generator.next().then((data) => generator.send(data));
}
  • Less callbacks == cleaner code
  • Can integrate with promises well
  • Lazy evaluation

The Triumvirate of Rendering

  • Backbone—Event emitters
  • Angular—Dirty checking
  • Ember—Run loop
  • Awkward to use
  • Difficult to debug
  • Doesn't scale
  • Full of magic

Object.observe

...the Angular team reported last year that their bindings saw a 20-40x increase in speed when using an early version of it.
Addy Osmani

Watches for changes on an object, and notify the bound function that something has changed, passing in:

  • the object that changed
  • the property that changed
  • what it changed from
  • how it changed (e.g. new property, updated, deleted, etc)
Object.observe(post, function({ type, name, object, oldValue }) {
    templates.update(object, name);
});

Array.observe

For people who have found the various custom implementations awkward and error prone

Array.observe(posts, function() {
    // watch for changes in an array.
});
  • Built into JS
  • No dirty checking means no slowdown at scale
  • No custom API for getting/setting properties
  • No custom API for adding removing array elements
  • Works automatically with other libraries:
    • D3
    • underscore
    • lo-dash

Weak Maps

How do you go from object to element?

  • Store a hash map of object elements:
    • fast
    • memory leaks
  • Traverse the DOM every time:
    • no memory leaks
    • slow

allow you to link one element to another without creating memory leaks

  • Create a lookup from object to DOM element
  • Don't store a reference to this lookup in the garbage collector
  • When the object disappears, remove it from the WeakMap
  • Fast
  • No memory leaks
var bindings = new WeakMap;
templates.update = function(object, property) {
    let element = bindings.get(object);
    if (element) element.textContent = object[property];
}

Template Strings

  • Backbone has no templating system at all
  • Most are fairly heavyweight
  • String concatenation in JS sucks
  • Multiline strings
  • Escaping things manually
var multiLineString = ['This',
'is my',
'',
'multiline ' + stringType,
'',
'',
'String ☺'].join('\n');
let multiLineString = `This
is my

multiline ${stringType}


String ☺`
escapeHTML`<h1>${title}</h1>
<section>${body}</section>`;

function escapeHTML(strings, ...values) {
    return interleave(strings, values.map(escape));
}
  • Lightweight templating
  • Directly in JS
  • Straightforward syntax
  • Custom rendering behaviour

New Features for Rendering

  • Easy to use
  • Scales
  • Less memory leaks
  • Lightweight
  • Extensible for frameworks to build on top of

When can I use it?

  • Start to see implementations in Chrome V8 and Firefox
  • Node.js users can already use parts (behind a command line flag)
  • Realistically, 2014
  • If you need to support old browsers: never

Useful Libraries

Any Questions?

Website: bengillies.net

Twitter: @bengillies