Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Performance on RAILs

Paul Lewis
November 23, 2015

Performance on RAILs

This is my talk from Nordic.js and BeyondConf.

Nordic.js Video: https://www.youtube.com/watch?v=uJMA2n4RL6s

Looking at web performance advice can be overwhelming: everything comes with caveats, disclaimers, and sometimes one piece of advice can seem to actively contradict another.

Phrases like “the DOM is slow” or “always use CSS animations!” make for great headlines, but the truth is often far more nuanced.

In this session we'll look at how to think holistically about performance, and how to prioritise optimisation work that your users will notice and appreciate.

Paul Lewis

November 23, 2015
Tweet

More Decks by Paul Lewis

Other Decks in Technology

Transcript

  1. SUCCESS PERFORMANCE “Fast is good, faster is better” - every

    bit of perf research Painfully slow user quits Bearable user persists Smooth user delight @aerotwist
  2. The user feels an instantaneous response. Any longer and the

    connection between action and reaction is broken. 0.1 SECONDS http://www.nngroup.com/articles/response-times-3-important-limits/ @aerotwist
  3. The user feels an instantaneous response. Any longer and the

    connection between action and reaction is broken. 0.1 SECONDS The user’s flow of thought is seamless. Beyond it, the user loses focus and attention. 1 SECOND http://www.nngroup.com/articles/response-times-3-important-limits/ @aerotwist
  4. The user feels an instantaneous response. Any longer and the

    connection between action and reaction is broken. 0.1 SECONDS The user’s flow of thought is seamless. Beyond it, the user loses focus and attention. 1 SECOND You’ve lost the user’s attention. 10 SECONDS http://www.nngroup.com/articles/response-times-3-important-limits/ @aerotwist
  5. The user feels an instantaneous response. Any longer and the

    connection between action and reaction is broken. 0.1 SECONDS @aerotwist
  6. Visual changes feel smooth and consistent. 16 MILLISECONDS Any variation

    in frame rate will be disconcerting to the user. @aerotwist
  7. Allows for responding to user interaction. Any longer and we

    may not be responsive. 50 MILLISECONDS @aerotwist
  8. The user’s flow of thought is seamless. Beyond it, the

    user loses focus and attention. 1 SECOND @aerotwist
  9. // Response: 100ms. this.sideNavToggleButton.addEventListener('click', () => { console.time('side-nav-tap'); this.toggleSideNav(); //

    Wait until the frame ships. requestAnimationFrame( _ => { console.timeEnd('side-nav-tap'); }); } );
  10. // Response: 100ms. this.sideNavToggleButton.addEventListener('click', () => { console.time('side-nav-tap'); this.toggleSideNav(); //

    Wait until the frame ships. requestAnimationFrame( _ => { console.timeEnd('side-nav-tap'); }); } );
  11. // Response: 100ms. this.sideNavToggleButton.addEventListener('click', () => { console.time('side-nav-tap'); this.toggleSideNav(); //

    Wait until the frame ships. requestAnimationFrame( _ => { console.timeEnd('side-nav-tap'); }); } );
  12. Simple responses are usually well under 100ms. Use console.time /

    timeEnd to track response times. RESPONSES @aerotwist
  13. // Animation: 60fps / 16ms per frame. showDetailsView () {

    console.time('heroTransitionShow'); this.heroImage.classList.add('hero-image--visible'); let onHeroAnimationFinished = () => { console.timeEnd('heroTransitionShow'); this.heroImage.removeEventListener('transitionend', onSideNavOpen); } // Listen for the end of the animation. this.heroImage.addEventListener(‘transitionend', onSideNavOpen); }
  14. // Animation: 60fps / 16ms per frame. showDetailsView () {

    console.time('heroTransitionShow'); this.heroImage.classList.add('hero-image--visible'); let onHeroAnimationFinished = () => { console.timeEnd('heroTransitionShow'); this.heroImage.removeEventListener('transitionend', onSideNavOpen); } // Listen for the end of the animation. this.heroImage.addEventListener(‘transitionend', onSideNavOpen); }
  15. // Animation: 60fps / 16ms per frame. showDetailsView () {

    console.time('heroTransitionShow'); this.heroImage.classList.add('hero-image--visible'); let onHeroAnimationFinished = () => { console.timeEnd('heroTransitionShow'); this.heroImage.removeEventListener('transitionend', onSideNavOpen); } // Listen for the end of the animation. this.heroImage.addEventListener(‘transitionend', onSideNavOpen); }
  16. Animate with transforms and opacity where you can. FLIP expensive

    properties when coupled with Responses. ANIMATIONS Be super cautious; avoid work during scrolls. @aerotwist
  17. voicememos-core.css INLINED record-something.svg 1 2 voicememos-record.js voicememos-record.css voicememos-core.js 3 voicememos-edit.js

    voicememos-edit.css 4 voicememos-list.js voicememos-list.css 5 voicememos-details.js voicememos-details.css
  18. self.onfetch = function(event) { event.respondWith( // Check the cache... caches.match(request).then(

    function(response) { // Return it if you got it. if (response) return response; // Otherwise... }); ); };
  19. self.onfetch = function(event) { event.respondWith( // Check the cache... caches.match(request).then(

    function(response) { // Return it if you got it. if (response) return response; // Otherwise... }); ); };
  20. self.onfetch = function(event) { event.respondWith( // Check the cache... caches.match(request).then(

    function(response) { // Return it if you got it. if (response) return response; // Otherwise... }); ); };
  21. self.onfetch = function(event) { event.respondWith( // Check the cache... caches.match(request).then(

    function(response) { // Return it if you got it. if (response) return response; // Otherwise... }); ); };
  22. self.onfetch = function(event) { event.respondWith( // Check the cache... caches.match(request).then(

    function(response) { // Return it if you got it. if (response) return response; // Otherwise... }); ); };
  23. return fetch(request).then(function(response) { // Anything not A-OK or opaque (CORSy

    stuff) we return. if (response.status !== 200 && response.status !== 0) return response; // Clone and cache the request and response. var responseToCache = response.clone(); var requestToCache = request.clone(); caches.open('le-cache').then(function(cache) { cache.put(requestToCache, responseToCache).catch(function(err) { console.warn(err.message); }); }); return response; });
  24. return fetch(request).then(function(response) { // Anything not A-OK or opaque (CORSy

    stuff) we return. if (response.status !== 200 && response.status !== 0) return response; // Clone and cache the request and response. var responseToCache = response.clone(); var requestToCache = request.clone(); caches.open('le-cache').then(function(cache) { cache.put(requestToCache, responseToCache).catch(function(err) { console.warn(err.message); }); }); return response; });
  25. return fetch(request).then(function(response) { // Anything not A-OK or opaque (CORSy

    stuff) we return. if (response.status !== 200 && response.status !== 0) return response; // Clone and cache the request and response. var responseToCache = response.clone(); var requestToCache = request.clone(); caches.open('le-cache').then(function(cache) { cache.put(requestToCache, responseToCache).catch(function(err) { console.warn(err.message); }); }); return response; });
  26. return fetch(request).then(function(response) { // Anything not A-OK or opaque (CORSy

    stuff) we return. if (response.status !== 200 && response.status !== 0) return response; // Clone and cache the request and response. var responseToCache = response.clone(); var requestToCache = request.clone(); caches.open('le-cache').then(function(cache) { cache.put(requestToCache, responseToCache).catch(function(err) { console.warn(err.message); }); }); return response; });
  27. return fetch(request).then(function(response) { // Anything not A-OK or opaque (CORSy

    stuff) we return. if (response.status !== 200 && response.status !== 0) return response; // Clone and cache the request and response. var responseToCache = response.clone(); var requestToCache = request.clone(); caches.open('le-cache').then(function(cache) { cache.put(requestToCache, responseToCache).catch(function(err) { console.warn(err.message); }); }); return response; });
  28. Inline app shell CSS. Lazy-load (async, defer) everything else. Far-future

    cache JS, CSS, images etc. LOAD Use a Service Worker. @aerotwist
  29. var tasks = []; var MIN_TASK_TIME = 1; function handleTasks(deadline)

    { while (deadline.timeRemaining() > MIN_TASK_TIME && tasks.length) processTask(tasks.pop()); if (tasks.length) requestIdleCallback(handleTasks); } requestIdleCallback(handleTasks);
  30. var tasks = []; var MIN_TASK_TIME = 1; function handleTasks(deadline)

    { while (deadline.timeRemaining() > MIN_TASK_TIME && tasks.length) processTask(tasks.pop()); if (tasks.length) requestIdleCallback(handleTasks); } requestIdleCallback(handleTasks);
  31. var tasks = []; var MIN_TASK_TIME = 1; function handleTasks(deadline)

    { while (deadline.timeRemaining() > MIN_TASK_TIME && tasks.length) processTask(tasks.pop()); if (tasks.length) requestIdleCallback(handleTasks); } requestIdleCallback(handleTasks);
  32. var tasks = []; var MIN_TASK_TIME = 1; function handleTasks(deadline)

    { while (deadline.timeRemaining() > MIN_TASK_TIME && tasks.length) processTask(tasks.pop()); if (tasks.length) requestIdleCallback(handleTasks); } requestIdleCallback(handleTasks);
  33. var tasks = []; var MIN_TASK_TIME = 1; function handleTasks(deadline)

    { while (deadline.timeRemaining() > MIN_TASK_TIME && tasks.length) processTask(tasks.pop()); if (tasks.length) requestIdleCallback(handleTasks); } requestIdleCallback(handleTasks);
  34. var tasks = []; var MIN_TASK_TIME = 1; function handleTasks(deadline)

    { while (deadline.timeRemaining() > MIN_TASK_TIME && tasks.length) processTask(tasks.pop()); if (tasks.length) requestIdleCallback(handleTasks); } requestIdleCallback(handleTasks);
  35. Use it for any non-essential work e.g. analytics. Think in

    small, deterministic operations. IDLE Think defensively. @aerotwist
  36. Udacity: Browser Rendering Optimization bit.ly/rail-udacity Web Fundamentals: Performance bit.ly/fundamentals-perf Voice

    Memos voice-memos.appspot.com aerotwist.com/blog/voice-memos/ Udacity: Critical Rendering Path bit.ly/crp-udacity @aerotwist