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!
- Build full bundle using whatever libs you need
- Use the web application via code coverage proxy
- 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)
})
)
})
Demos: https://glebbahmutov.com/bottle-service, instant-todo.herokuapp.com
The library: bahmutov/bottle-service
Blog post with examples: Instant web application
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?
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