Choosing the right Frontend Javascript Framework

React

Polymer

Angular

Ember

Vue

Aurelia

The Why

  • There are a lot!
  • Different problems, different solutions
  • Complexity Matters
  • No silver bullet (yet?!)

jQuery

(Cue Gasp)

Not Actually The Devil

A push for speed, reduction in bloat!

$.get('http://myResource/api/getThings')
.then(() => {})
.catch(console.error);

3.0 is A+ Promise Standard Compliant

Offers reasonable slim-version, plus pick your-own modules variable

Feel free to ignore this screenshot of patchnotes

Super useful for lightweight work

$(async () => {
  const starterData = await $.get('a/resource');
  $('#app .dataContainer').text(starterData);
});

Animation Improvements

"jQuery 3.0, if the browser supports it, uses requestAnimationFrame API to perform animations. The API uses GPU to perform animations. This method is powerful because it’s faster and smoother while performing animations, and on mobile devices, it’s a battery saver."- jQuery 3.0 Release Notes

Custom Selectors Improvement

"jQuery offers some custom selectors (like :visible and :hidden) that apparently look like a CSS selector, but are resolved by jQuery selector engine.

The developers found that with these selectors, some extra work can be skipped if used multiple times in the same document. After they optimized it in jQuery 3, the result is now ~17 times faster." - jQuery 3.0 Release Notes

Overall?

Has a place. Just a small and niche one.

Knockout.js

2-Way Data Binding Galore

Still Relevant?

Oldie but goodie

Simple API

<div data-bind="text: potato" />

ko.applyBindings({
  potato: 'I am a binding!'
});

Easy Template Helpers

<div data-bind="foreach: things">
  <span data-bind="if: showThing && $data % 2 === 0">
    <div data-bind="text: 'Thing!: ' + $data" />
  </span>
</div>

ko.applyBindings({
  showThing: true,
  things: ko.observableArray([1,2,3,4])
});

Reasonable Complexity Scale


var viewModelOne = {
  some: ko.observable('data')
};

var viewModelTwo = {
  some: ko.observable('other data')
};

window.addEventListener('hashchange', () => {
  ko.cleanNode(window);
  if(location.hash === '1')
    ko.applyBindings(viewModelOne);
  else if(location.hash === '2')
    ko.applyBindings(viewModelTwo);
});

IE6 Compatible

:/

Unopinionated

var viewModelOne = function() {
  this.core = 'core';
};

var viewModelTwo = {
  core: 'core'
};

ko.applyBindings(
  new viewModelOne()
);

ko.applyBindings(
  viewModelTwo
);

2-Way Binding


<input
  type="text"
  data-bind="textInput: username" />

var viewModel = {
  username: ko.observable('')
};

ko.applyBindings(viewModel);

Overall?

Useful for small -> medium projects that require older browser support

 

Not a framework for ambitious web applications

Backbone.js

Irrelevant?

Old Framework is Old

Template Syntax is 'unique'

var TodoView = Backbone.View.extend({
  template: _.template($('#item-template').html())
});

Clunky API

var Books = Backbone.Collection.extend({
  url: '/books'
});

Designed around CRUD

Older style is more rigid

var object = {};

_.extend(object, Backbone.Events);

object.on("alert", function(msg) {
  alert("Triggered " + msg);
});

object.trigger("alert", "an event");

You leverage and extend Backbone objects

Does not abstract intuitively

Overall?

Irrelevant, clunky, and outta date

Angular 1.x

Overall?

No

Angular2

The Best Thing Since Sliced Bread!

Not Really

They're keeping the Angular 1.x branding, and angularjs.com still takes you to the 1.x site.

Typescript!

class Greeter {
    greeting: string;
    constructor (message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}  

Angular2 Components

import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  template: `
    <h1>{{title}}</h1>
    <h2>My favorite hero is: {{myHero}}</h2>
    `
})
export class AppComponent {
  title = 'Tour of Heroes';
  myHero = 'Windstorm';
}

Angular CLI

ng serve --host 0.0.0.0 --port 4201

ng generate component my-new-component
ng g component my-new-component # using the alias

# components support relative path generation
# if in the directory src/app/feature/ and you run
ng g component new-cmp
# your component will be generated in src/app/feature/new-cmp
# but if you were to run
ng g component ../newer-cmp
# your component will be generated in src/app/newer-cmp

Why OOP and DI?

public description = 'DI';

constructor(public engine: Engine, public tires: Tires) { }
public engine: Engine;
public tires: Tires;
public description = 'No DI';

constructor() {
  this.engine = new Engine();
  this.tires = new Tires();
}
// Simple car with 4 cylinders and Flintstone tires.
let car = new Car(new Engine(), new Tires());

Templates

<p>{{myVar}}</p> //Good!

<button (click)="deleteHero()">Delete hero</button> //What?


//Weird
<button (click)="onSave($event)">Save</button>
<button *ngFor="let hero of heroes" (click)="deleteHero(hero)">{{hero.name}}</button>
<form #heroForm (ngSubmit)="onSubmit(heroForm)"> ... </form>


//Please stop
<!-- Bind button disabled state to `isUnchanged` property -->
<button [disabled]="isUnchanged">Save</button>

//No why
<button [attr.aria-label]="actionName">{{actionName}} with Aria</button>

Two-Way Binding?

//Component.ts
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
  selector: 'my-sizer',
  template: `
  <div>
    <button (click)="dec()" title="smaller">-</button>
    <button (click)="inc()" title="bigger">+</button>
    <label [style.font-size.px]="size">FontSize: {{size}}px</label>
  </div>`
})
export class SizerComponent {
  @Input()  size: number | string;
  @Output() sizeChange = new EventEmitter<number>();
  dec() { this.resize(-1); }
  inc() { this.resize(+1); }
  resize(delta: number) {
    this.size = Math.min(40, Math.max(8, +this.size + delta));
    this.sizeChange.emit(this.size);
  }
}


//In Use
<my-sizer [(size)]="fontSizePx"></my-sizer> //Note the [()] syntax for 2-way binding
<div [style.font-size.px]="fontSizePx">Resizable Text</div>

Overall?

  • Strong community and corporate backing
  • Patterns outside the current norm
  • Angular 3? 4?

Polymer 2.0

Drinking the ES6+ Koolaid

Welcome to `class`

  • Leans very heavily on ES6 classes
  • Have to manually declare the <dom-element />
  • Based on native web-component spec

DIY like a box of legos


  // Define the class for a new element called custom-element
  class CustomElement extends Polymer.Element {
    static get is() { return "custom-element"; }
    constructor() {
        super();
        this.textContent = "I'm a custom-element.";
      }
  }
  // Register the new element with the browser
  customElements.define(CustomElement.is, CustomElement);

Create your own Shadow-DOM!

<dom-module id="dom-element">

  <template>
    <p>I'm a DOM element. This is my local DOM!</p>
  </template>

  <script>
    class DomElement extends Polymer.Element {
      static get is() { return "dom-element"; }
    }
    customElements.define(DomElement.is, DomElement);
  </script>

</dom-module>

Now I'm just Copy and Pasting

<dom-module id="picture-frame">

  <template>
    <!-- scoped CSS for this element -->
    <style>
      div {
        display: inline-block;
        background-color: #ccc;
        border-radius: 8px;
        padding: 4px;
      }
    </style>
    <div>
      <!-- any children are rendered here -->
      <slot></slot>
    </div>
  </template>

  <script>
    class PictureFrame extends Polymer.Element {
      static get is() { return "picture-frame"; }
    }
    customElements.define(PictureFrame.is, PictureFrame);
  </script>

</dom-module>

I heard you like JSX...

Mustache-like syntax for variables in templates

  <template>
    <!-- bind to the "owner" property -->
    This is <b>{{owner}}</b>'s name-tag element.
  </template>

2-Way Binding... and more <template> tags

<link rel="import"  href="https://polygit.org/polymer+2.0.0-rc.2/iron-input+polymerelements+:2.0-preview/shadycss+webcomponents+1.0.0-rc.2/components/iron-input/iron-input.html">
  <template>
    <iron-input bind-value="{{owner}}">
      <input is="iron-input" placeholder="Your name here...">
    </iron-input>
  </template>

You have to import iron-input :/

Building the App

  • Has a vulcanizer, which is okay
  • HTML imports are ugly now, but will be better with HTTP/2
  • Vulcanizer will multi-import things, doesn't know how to cleverly resolve same dependencies

Overall?

Avoid for now

Ember.js

Hamsters Everywhere

Like an overbearing family member

Ember CLI

npm install -g ember-cli@2.12
npm install -g bower
brew install watchman (OSX Only)
npm install -g phantomjs-prebuilt

Then...
ember new my-new-app
cd my-new-app
ember server

Hyper Opinionated

  • Forced, effective, abstractions
  • Ember-way or the high-way
  • Easily maintainable long term
  • Easy to bring new Ember devs

Handlebars

<p>
  Hello,
  <strong> {{firstName}} {{lastName}}</strong>!
</p>


import Ember from 'ember';

export default Ember.Controller.extend({
  firstName: 'Trek',
  lastName: 'Glowacki'
});

Ember Store

//route.js
export default Ember.Route.extend({
  model(params) {
    return this.store.findRecord('user', params.id);
  }
});


//model.js
import DS from 'ember-data';

export default DS.Model.extend({
  firstName: DS.attr('string'),
  lastName: DS.attr('string'),
  articles: DS.hasMany(),

  fullName: Ember.computed('firstName', 'lastName', function() {
    return `${this.get('firstName')} ${this.get('lastName')}`;
  }),
});

Glimmer Rendering Engine

  • Reasonable fast on initial render
  • Ultra-fast on re-render
  • Isolated, can be used like React

Overall?

  • Powerful, Robust, but Very Opinionated
  • High Learning Curve
  • "Fattest" framework next to Angular 2
  • Hamsters

Vue.js

That thing you've been hearing about!

Faster Than React!!1!

0.02 seconds faster than React

Quick to get going

<script src="https://unpkg.com/vue"></script>

<div id="app">
  {{ message }}
</div>

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

Templates are okay

{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
  • Single expression support
  • Mustache
  • v-*

Two-Way Data Binding

<div id="app">
  <p>{{ message }}</p>
  <input v-model="message">
</div>

var app6 = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
});

Optional

Abstracting?

var data = { counter: 0 }
Vue.component('simple-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  // data is technically a function, so Vue won't
  // complain, but we return the same object
  // reference for each component instance
  data: function () {
    return data
  }
})
new Vue({
  el: '#example-2'
})

Overall?

Kinda meh.

CLI?

Young.

Aurelia

What?

Oh yeah, we had a talk on it!

Components and Views!

export class Todo {
  constructor(description) {
    this.description = description;
    this.done = false;
  }
}
export class App {
  constructor() {
    this.heading = "Todos";
    this.todos = [];
    this.todoDescription = '';
  }
  addTodo() {
    if (this.todoDescription) {
      this.todos.push(new Todo(this.todoDescription));
      this.todoDescription = '';
    }
  }
  removeTodo(todo) {
    let index = this.todos.indexOf(todo);
    if (index !== -1) {
      this.todos.splice(index, 1);
    }
  }
}
<template>
  <h1>${heading}</h1>

  <form submit.trigger="addTodo()">
    <input type="text" value.bind="todoDescription">
    <button type="submit">Add Todo</button>
  </form>

  <ul>
    <li repeat.for="todo of todos">
      <input type="checkbox" checked.bind="todo.done">
      <span>
        ${todo.description}
      </span>
      <button click.trigger="removeTodo(todo)">
        Remove
      </button>
    </li>    
  </ul>
</template>
  • Aurelia-specific dom event bindings

  • ${variable} in templates :/
  • Unidirectional Data Flow

Website Panders Paid Resources

  • DOM, not Shadow-DOM

  • Web-Components+

  • No weird licenses

I stole this slide

Overall?

Kinda Young

Keep an eye out

React

If you're not using React, you're not living

I swear I'm not a shill!

Quickstart?

Just install...

  • Webpack
  • Babel
  • All necessary presets
  • Transformers
  • Plugins

JSX

class App extends Component {
  render() {
    return (
      <div>
        <MyComponent />
      </div>
    );
  }
}

Component all the things

Unidirectional Data Flow

  • All data stored in a single object
  • Create updated state
  • Send state to React
  • React updates view & state

 

No two-way binding at all!

Can't share state between components

Solutions?

  • Flux
  • Redux
  • MobX
  • Cerebral
  • GborthagubX

Beloved Overlord Facebook

"Weird License"

Javascript!

class Core extends Component {

  render() {
    return (
      <div className='coreContainer'>
        {this.props.children}
        {
            [1,2,3,4].map(i => <div key={i}>Counter: {i}</div>)
        }
      </div>
    );
  }
}

export default Core;

Overall?

  • Popular and Powerful
  • Limited base tooling
  • Very Javascript-y

Inferno

It's React

But Faster!

  • Faster than react, omg
  • React knowledge transfers
  • No weird Facebook licensing
  • Simplified API compared with react
  • Inbuilt state-managers like Redux and MobX
  • Built-in Isomorphism

Honorable Mentions

  • Meteor.js
  • hyperapp
  • Preact

Contact

/krishnaglick on Github

@krishnaglick on Twitter

@prometheus on ODevs Slack

Choosing the right Frontend Javascript Framework

By Krishna Glick

Choosing the right Frontend Javascript Framework

http://orlandocodecamp.com/Sessions/Details/53

  • 344