Which JavaScript framework should I use next?

Dr. Gleb Bahmutov PhD

source: http://www.slideshare.net/Hugeinc/javascript-state-of-the-union-2015-english

Angular vs React: there will be blood

Need for Speed

Ready, Set, Go!

Browser profile

Every "X is faster than Y" comparison

X vs Y performance: both lose to vanilla JS

Q: Why use a framework at all?

Joe Gregorio - No more JS frameworks

Right hammer for the right nail

  • Frameworks solve the common case
  • They solve data flow, display, architecture
  • The solution is good enough

Q: Which framework is the best?

Q: What features should a modern framework have?

Speed

"Web Workers" by Nolan Lawson @nolanlawson, EmpireJS 2016

60 fps? Cannot do much for each frame

Page runs in a single thread - everything is competing for 1/60 of a second

Single thread cannot compute AND paint!

Solution: multiple threads

What can Web Workers do?

  • JavaScript
  • JSON
  • IndexedDB
  • AJAX
  • ...but not DOM 😢
  • Virtual DOM!

Source: http://www.pocketjavascript.com/blog/2015/11/23/introducing-pokedex-org

60fps application even on a mediocre Android phone

React with Web Workers

Angular 2 with Web Workers

Web Workers: must have

Size

Pokedex.org bundle sizes

main.js 24 kB
worker.js 90 kB
serviceWorker.js 16 kB

How small can it be?

How small can it be?

How small can it be?

How small can it be?

Solution 1: Start small

Feather app

by Henrik Joreteg

  • Virtual-dom rendering in WebWorker
  • Simple routing
  • <9KB min + gzip!

this is plain awesome

Solution 2: Start big

Not tested - not included!

  1. Build full bundle using whatever libs you need
  2. Use the web application via code coverage proxy
  3. Remove uncovered code from the bundle

code coverage proxy was-tested

  • RxJs + Cycle.js + virtual-dom
  • Original bundle 590KB
  • 44KB min + gzip!

this is work in progress

  • CommonJS code - use coverage information

  • ES6 modules - use tree shaking

// calc.js
export const add = (a, b) => a + b
export const sub = (a, b) => a - b

// main.js
import { add } from './calc'
...
// bundle.js
const add = (a, b) => a + b
...

rollup main.js

Modular design for slim builds

Hydration

Web Apps

Q: How to start them quickly?

Using CDN, parallel downloads, caching, small images, above the fold, etc.

Ready, set, go!

HTML

JS

CSS

Ready, set, go!

HTML

JS

CSS

Framework

Ready, set, go!

HTML

JS

CSS

Framework

app

Ready, set, go!

HTML

JS

CSS

Framework

app

get data

Ready, set, go!

HTML

JS

CSS

Framework

app

draw

get data

Ready, set, go!

HTML

JS

CSS

Framework

app

draw

get data

HTML

Cached by the browser

You should cache

JS

CSS

Framework

app

draw

get data

HTML

This part can be expensive!

startup

PivotalTracker reloading without data changes

My observations

The data does not change between the reloads*

I do not interact with the page in the first 500ms

Even more frustrating

- Initial HTML loads

- Application code loads

<html>
  <script src="app.js"></script>
  <body>
    <h1>My awesome app</h1>
    <div id="app"></div>
    <footer>...</footer>
  </body>
</html>

Even more frustrating

- App loads data

- App updates page

<html>
  <script src="app.js"></script>
  <body>
    <h1>My awesome app</h1>
    <div id="app"></div>
    <footer>...</footer>
  </body>
</html>

Even more frustrating

- App loads data

- App updates page

Sudden change in the page!

<html>
  <script src="app.js"></script>
  <body>
    <h1>My awesome app</h1>
    <div id="app">
      <ul>
        <li>Clean my room</li>
        <li>Learn Italian</li>
        <li>Finish the slides</li>
      </ul>
    </div>
    <footer>...</footer>
  </body>
</html>

Example

source: glebbahmutov.com/hydrate-vue-todo/ (no hydration)

Solution 1: Build time rendering

  • Render the initial HTML during the build

  • Send to client on load

Solution 2: Cached HTML

  • Save the last HTML client-side

  • Reuse on load

app

HTML

data

load

load

render

App startup

Cache

app

HTML

data

load

load

render

Same data => same HTML

Cache

  • Load cached HTML and show right away

  • Continue loading web app normally

During startup

When web app loads, the "dry" HTML becomes live, hence "hydration"

User sees "dry" HTML at first

Example

source: glebbahmutov.com/hydrate-vue-todo/ (with hydration)

Hydration

Single small JS library bahmutov/hydrate-app

<div id="app">
  ...
</div>
<script src="hydrate-app.js" id="app"></script>

Works with any web framework

// hydrate-app gives you window.bottle
bottle.drink()  // app has loaded and ready to take over
bottle.refill() // save DOM for next reload

Not good enough

There is a flash

<body>
  <header>...</header>
  <div id="app"></div>
  <script src="hydrate-app.js"></script>
</body>
const html = localStorage.get ...
$('#app').innerHTML = '...'

Self-rewriting page is never going to be as smooth as server-side rendering

Solution 3: ServiceWorker

Save generated HTML client-side inside ServiceWorker

Bottle-service

browser

ServiceWorker

<html>
<body>
  <header>...</header>
  <div id="app">
   <ul>
     <li>Clean my room</li>
     ...
   </ul>
  </div>
  <footer>...</footer>
</body>
</html>
<ul>
  <li>Clean my room</li>
  ...
</ul>

Cache

Bottle-service

Server

browser

ServiceWorker

<html>
<body>
  <header>...</header>
  <div id="app"></div>
  <footer>...</footer>
</body>
</html>
<html>
<body>
  <header>...</header>
  <div id="app">
   <ul>
     <li>Clean my room</li>
     ...
   </ul>
  </div>
  <footer>...</footer>
</body>
</html>
<ul>
  <li>Clean my room</li>
  ...
</ul>

Cache

Inserts cached HTML fragment into response body HTML

reload()

Bottle-service

self.addEventListener('fetch', function (event) {
  event.respondWith(
    fetch(event.request)
      .then(function (response) {
        // merge HTML
        var responseOptions = {
          status: 200,
          headers: {
            'Content-Type': 'text/html charset=UTF-8'
          }
        }
        return new Response(resultHtml, responseOptions)
      })
  )
})

Bottle-service

Works with any web framework

ServiceWorker

Start app quickly

Offline

API

server

database

Best case scenario

API

server

database

Real life: partial or permanent offline

Synching data yourself is HARD

replication

server

database

Let DB synch for you

database

Web app only works with in-browser PouchDB

Let DB synch for you

PouchDB can synch with another PouchDB / CouchDB / *

replication

server

database

database

Let DB synch for you

PouchDB can synch when a connection is available

replication

server

database

database

replication

server

database

Offline first API

database

Hoodie API for offline web apps rocks

hoodie

Offline web apps

Reactive

work: Kensho

event analysis and statistics for financial markets

Boston and New York

Kensho dashboard app

data

Kensho dashboard app

Kensho dashboard app

finished?

$http.get(...).then(...)

Kensho dashboard app

finished?

$http.get(...).then(...)

.then(...)

* times

1. Event emitters are great at broadcasting without expecting an answer

2. Promises are great for single time actions

Kensho dashboard app

stream

stream operation

* times

stream

stream

stream

RxJS for the win!

stream

stream

Web framework should handle streams of events

Purity

function add(a, b) {
  return a + b
}

Remember pure functions?

Simple to write

Simple to test

Simple to reuse

const F = compose(f, g, h)
F(x)

pure

pure

Can a web application be pure?

Getting dirty

function main() {
  window.title = 'hi'
}

Pure function loophole

function main() {
  const title = 'hi'
  return function foo() {
    window.title = title
  }
}

// pure

// dirty

const app = main()
app()

// pure

// dirty

A little better

function main(win) {
  const title = 'hi'
  return function foo() {
    win.title = title
  }
}

// pure

// dirty

const app = main(window)
app()

// pure

// dirty

Cycle.js = pure FP + RxJs

function main({DOM}) {
  const keyups$ = DOM.select(...)
  return {
    DOM: keyups$.map(...vdom...)
  }
}

all inputs are streams

all outputs are streams

Cycle.js = pure FP + RxJs

function main({DOM}) {
  const keyups$ = DOM.select(...)
  return {
    DOM: keyups$.map(...vdom...)
  }
}

Main function is pure!

Pure web components are simpler to write, test and reuse

Real time

We treat web apps as if they were turn-based games

User clicks button

Server responds

Page renders result

Does not work like the real world

Can your web framework handle multiple async events?

New realtime backends: feathers, horizon, IoT need real time frontends

Simple to try

Backbone

Angular 1

React

Angular 1

Can I try framework X on a small component inside my current app?

Things to look for:

  • DOM abstractions
  • Modular design for smaller size
  • Hydration for quick start ups 
  • Offline support
  • Reactive streams
  • Functional purity
  • Real time support
  • Simple to try

Pick the framework:

that makes your team more productive and happy

Thank you

these slides:   slides.com/bahmutov/buzzjs

all links:     glebbahmutov.com/blog/buzzjs