load_time > 6000 &&
Math.random() < .4 ?
fail() : profit()
while( wait_time % 500 )
(╯°□°)╯︵ ┻━┻
http://glinden.blogspot.com/2006/11/marissa-mayer-at-web-20.html
http://glinden.blogspot.com/2006/11/marissa-mayer-at-web-20.html
2015
2015
we’ve decided to take site speed into account in our search rankings
https://webmasters.googleblog.com/2010/04/using-site-speed-in-web-search-ranking.html
https://blog.cloudflare.com/ttfb-time-to-first-byte-considered-meaningles
how long it takes for a webpage to be loaded and usable by a browser
https://www.littlebizzy.com/blog/ttfb-meaningless
Performance
Perception
Performance
Perception
Delay | User reaction |
---|---|
0 - 100 ms | Instant |
100 - 300 ms | Feels sluggish |
300 - 1000 ms | Acceptable for changing views |
> 1 s | Mental context switch |
> 10 s | Frustrated, leave, may or may not return |
https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/rail
https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/rail
https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/rail
https://developers.google.com/web/fundamentals/performance/rendering/
https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/rail
https://developers.google.com/web/tools/chrome-devtools/profile/evaluate-performance/rail
Don't make it so pretty that it doesn't work for the end user
Devs must remember...
...what happens in the intermediate steps between receiving the HTML, CSS, and JavaScript bytes and the required processing to turn them into rendered pixels
load html > load resources > parse > display
https://developers.google.com/web/fundamentals/performance/critical-rendering-path
https://developers.google.com/web/fundamentals/performance/critical-rendering-path/analyzing-crp
https://developers.google.com/web/fundamentals/performance/critical-rendering-path/optimizing-critical-rendering-path
https://varvy.com/pagespeed/critical-render-path.html
https://css-tricks.com/fout-foit-foft
</body>
<div style="color:salmon">
require('juice')
require('inline-css')
proxy_cache_path /var/lib/nginx/cache levels=1:2 keys_zone=backcache:8m max_size=50m;
proxy_cache_key "$scheme$request_method$host$request_uri$is_args$args";
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
location / {
proxy_cache backcache;
proxy_cache_bypass $http_cache_control;
add_header X-Proxy-Cache $upstream_cache_status;
...
if ( contentChanged )
respond.status(200).send(content);
else
respond.status(304);
https://varvy.com/ifmodified.html
http://www.html5rocks.com/en/tutorials/service-worker/introduction/
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function(registration) {
console.log(
'ServiceWorker registered with scope: ',
registration.scope
);
}).catch(function(err) {
console.log(
'ServiceWorker registration failed: ',
err
);
});
}
chrome://inspect/#service-workers
var CACHE_NAME = 'my-app-cache-v1';
var urlsToCache = [
'/',
'/css/bundle.css',
'/js/bundle.js'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request);
}
)
);
});
https://docs.google.com/presentation/d/1GNLc4oRZzazq4Th8vsH3v5GekAbKWsxIXHbNtQFFG-c/present
<img src="logo.png" />
<img src="logo.242.png" />
<img src="logo.png#20160831" />
gzip on;
gzip_min_length 1000;
gzip_proxied expired
no-cache
no-store
private
auth;
gzip_types text/plain
application/xml
application/json;
Accept-Encoding: gzip, deflate
Content-Encoding: gzip
# Enable Nginx HttpGzipStaticModule
server {
...
gzip_static on;
}
Accept-Encoding: gzip, deflate
Content-Encoding: gzip
uncompressed: 106,272 bytes
mod_deflate: 28,732 bytes (27%)
gzip -9: 28,062 bytes (26%)
Zopfli: 27,033 bytes (25%)
https://github.com/google/zopfli
http://www.cambus.net/serving-precompressed-content-with-nginx-and-zopfli/
https://www.stevesouders.com/blog/2013/03/08/zopflinator/
server {
...
brotli on;
brotli_static on;
brotli_types text/plain application/xml application/json;
}
Accept-Encoding: brotli
https://github.com/google/ngx_brotli
https://quixdb.github.io/squash-benchmark/
ngx_brotli filter module - on the fly
ngx_brotli static module - precompressed
comp level | comp speed | decomp speed |
---|---|---|
zlib deflate 4 | 21.45 MiB/s | 96.16 MiB/s |
zlib gzip 4 | 20.71 MiB/s | 89.21 MiB/s |
brotli 1 | 23.94 MiB/s | 115.02 MiB/s |
https://www.devleague.com/css/bundle.css
origin
:443
https://www.devleague.com/css/bundle.css
https://www.devleague.com/js/bundle.js
https://www.devleague.com/images/asset1.png
https://www.devleague.com/images/asset2.png
https://www.devleague.com/images/asset3.png
https://www.devleague.com/images/asset4.png
https://www.devleague.com/images/asset5.png
https://www.devleague.com/images/asset6.png
https://www.devleague.com/images/asset7.png
https://www.devleague.com/images/asset8.png
https://www.devleague.com/images/asset9.png
https://www.devleague.com/images/asset10.png
https://www.devleague.com/images/asset11.png
https://www.devleague.com/images/asset12.png
https://www.devleague.com/images/asset13.png
https://www.devleague.com/images/asset14.png
https://www.devleague.com/images/asset15.png
https://www.devleague.com/images/asset16.png
Waiting...
Loading...
https://cdn1.devleague.com/css/bundle.css
origin
:443
https://cdn1.devleague.com/css/bundle.css
https://cdn1.devleague.com/js/bundle.js
https://cdn1.devleague.com/images/asset1.png
https://cdn1.devleague.com/images/asset2.png
https://cdn1.devleague.com/images/asset3.png
https://cdn1.devleague.com/images/asset4.png
https://cdn2.devleague.com/images/asset5.png
https://cdn2.devleague.com/images/asset6.png
https://cdn2.devleague.com/images/asset7.png
https://cdn2.devleague.com/images/asset8.png
https://cdn2.devleague.com/images/asset9.png
https://cdn2.devleague.com/images/asset10.png
https://cdn3.devleague.com/images/asset11.png
https://cdn3.devleague.com/images/asset12.png
https://cdn3.devleague.com/images/asset13.png
https://cdn3.devleague.com/images/asset14.png
https://cdn3.devleague.com/images/asset15.png
https://cdn3.devleague.com/images/asset16.png
Loading...
Loading...
Loading...
https://facebook.github.io/react/docs/environments.html
const React = require('react');
const ReactDOMServer = require('react-dom/server');
...
app.get('/', (req, res) => {
const element = React.createElement('div', null, 'Hello World!');
res.send(ReactDOMServer.renderToString(element));
});
function ngApp(req, res) {
let baseUrl = '/';
let url = req.originalUrl || '/';
let config: ExpressEngineConfig = {
directives: [ App ],
platformProviders: [
{provide: ORIGIN_URL, useValue: 'http://localhost:3000'},
{provide: BASE_URL, useValue: baseUrl},
],
providers: [
{provide: REQUEST_URL, useValue: url},
provideRouter(routes),
NODE_LOCATION_PROVIDERS,
NODE_HTTP_PROVIDERS,
],
async: true,
preboot: false
};
res.render('index', config);
}
app.get('/', ngApp);
app.get('/home', ngApp);
app.get('/about', ngApp);
https://universal.angular.io
https://universal.angular.io
facebook, twitter, etc.