Gleb Bahmutov PRO
JavaScript ninja, image processing expert, software quality fanatic
A DATA ANALYTICS AND MACHINE INTELLIGENCE COMPANY
I love solving AngularJS performance problems
< 16ms, Virtual DOM, WebWorkers
Using CDN, parallel downloads, caching, small images, above the fold, etc.
JS
CSS
Framework
app
draw
get data
HTML
Hosting / ops
App author
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!
nice
what?
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>
- 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>
- 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>
app
HTML
data
loads
renders
same
same
app
HTML
data
loads
renders
Load # N
- App loads data
- App updates page
Load # N + 1
Load HTML QUICKLY from localStorage
- load normally ...
Save data + HTML in localStorage
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
app
HTML
data
loads
loads
renders
<div id="app">
<ul>
<li>Clean my room</li>
<li>Learn Italian</li>
<li>Finish the slides</li>
</ul>
</div>
<div id="real-app" class="hidden">
</div>
hydrate.js loads cached "app" HTML
HTML
app
HTML
data
loads
loads
renders
<div id="app">
<ul>
<li>Clean my room</li>
<li>Learn Italian</li>
<li>Finish the slides</li>
</ul>
</div>
<div id="real-app" class="hidden">
<ul>
<li>Clean my room</li>
...
</ul>
</div>
Web app renders into hidden DOM element
HTML
hydrate.js loads cached "app" HTML
app
HTML
data
loads
loads
renders
bottle.drink()
Live web app content replaces static cached HTML snapshot
Progressive web app for $0.05
HTML
<div id="app">
<ul>
<li>Clean my room</li>
...
</ul>
</div>
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
The page HTML arrives ready to be rendered by the browser
The browsers are REALLY REALLY good at rendering static pages
ServiceWorker
Server
browser
Web Workers
ServiceWorker
Transforms
the response
Transforms
the request
Server
browser
service
worker
navigator.serviceWorker.register(
'app/my-service-worker.js')
Chrome, Opera
Firefox behind a flag
Must be https
self.addEventListener('install', ...)
self.addEventListener('activate', ...)
self.addEventListener('message', ...)
self.addEventListener('push', ...)
self.addEventListener('fetch', function (event) {
console.log(event.request.url)
event.respondWith(...)
})
// Cache API
browser | web worker | service worker |
---|---|---|
window | self | self |
localStorage | IndexedDB, Cache API (async) | IndexedDB, Cache API (async) |
XMLHttpRequest, fetch | XMLHttpRequest, fetch |
fetch |
document |
Promises, postMessage
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
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://instant-todo.gleb-demos.com, https://glebbahmutov.com/bottle-service
The library: bahmutov/bottle-service
Blog post with examples: Instant web application
If the server goes down in the forest ...
Stop treating browser like a child
browser
ServiceWorker
Request
Response
Server
Server
browser
ServiceWorker
Request
Response
express.js
http.ClientRequest
JavaScript
http.ServerResponse
JavaScript
browser
ServiceWorker
Server
express.js
Server
browser
ServiceWorker
express.js
http.ClientRequest(Request)
http.ServerResponse(Response)
express.js + middleware
ServiceWorker env
browserify
var express = require('express')
var app = express()
var url = require('url')
self.addEventListener('fetch', function (event) {
"app" is a regular Express.js application
const parsedUrl = url.parse(event.request.url)
var req = {
url: parsedUrl.href,
method: event.request.method,
body: body,
headers: {
'content-type': event.request.headers.get('content-type')
},
unpipe: function () {}
}
var res = { /* our mock Node http Server Response object */ }
event.respondWith(new Promise(function (resolve) {
app(req, res)
}))
})
Demo at: https://express-service.gleb-demos.com
The library: github.com/bahmutov/express-service
Blog post with examples: Run Express server in your browser
There is no client-side* JavaScript :)
<link rel="serviceworker" scope="/" href="/sw.js">
*after first ServiceWorker registration
$ npm i -g nativefier
$ nativefier https://express-service.gleb-demos.com/
Packaging app for platform darwin x64 using electron v1.1.3
App built to /Users/gleb/git/ExpressService-darwin-x64
Express-service stand alone desktop app
Mozilla SW recipes serviceworke.rs
Jake Archibald (Google) - Using ServiceWorker in Chrome today, The ServiceWorker is coming, look busy
Matt Gaunt (Google) - Unit testing ServiceWorker
Instant apps
Server in the browser
Code transpiling, coverage, testing
By Gleb Bahmutov
The line between the server and the modern browser is blurry. Transpiling code, rendering static markup, smart caching were traditionally done on the server. These features made the jump to the browser by using ServiceWorkers. Video at https://www.youtube.com/watch?v=5zOaN_FYXwk
JavaScript ninja, image processing expert, software quality fanatic