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

Lightning Fast Web Applications

Lightning Fast Web Applications

Do you want to load your web pages instantly? From cold load performance to repeating load performance, speed is key to attract your audience and sell your products. In this talk, we will explore how you can improve your page speed, and get the best from Web Vitals in your application with PWA, Offline and Service Workers.

- How soon can you load a page?
Performance plays a significant role in the modern web. Faster web sites engage and retain their users better than poor performing web sites

- How can you stay fast?
At a point a time your application can be the fastest version of itself. But by the time, with new additions to your app, changes in the internet, browsers even user behaviours, how can you stay fast?

- Nowadays, the average time to interactive duration is 
almost 10 seconds
From HTTP archive reports, it is easier to track the changes. Luckily for the last years, we have some decline in the trends, which is a good thing but we had times that websites were able to become interactive after 15-16 seconds.

- On an average website, we are loading over 70KB of CSS, 450 KB of JavaScript and at least 1 MB of images. When you have multiple of those files, you can think that our page load size is the multiplication of each type of assets. Some of those resources, either render-blocking resources that browsers need to wait to download or execute.

- How can I check those for my website?
To understand how our website performance, there are various tools that we can use.

- Lighthouse is the most simple one to use
The measurements can be tracked as lab data or field data. For the lab data, we can use the lighthouse as a tool. Which is easier to integrate to use the dev environment or having manual check.

Besides this to ensure the app performance, we need to continuously monitor to application in the real production environment.

- Let’s think we have an online shop
Since last year with the pandemic, we have seen that online platforms became digital flagships to continue to business

- Our application doesn’t have any optimisation not in the code level nor in server or CDN levels. We are building our application for legacy browsers, without any suggested minification or chunking strategy.

We build our application with Webpack and we are using React as the main dependency.

- We set some performance budgets for our app. Like having entry resources smaller than 170 KB in total to kick start the application.

- Minifying text-based assets will reduce 
download and loading times.

- Let's update our webpack to minimize our assets

- We have a little improvement on the performance but now our app becomes interactive in under 5 seconds

- Splitting a big single file into 
smaller easy to load chunks will ease code execution timing.

- Let's update our webpack optimization settings to have chunks. Also since our main dependency React / ReactDOM, in nature, we do not change them in every deployment of the app. So we can have a separate chunk for it, in the future we can have longer caching or CDN loading for it as further optimisations.

- Our performance is getting better slightly. But note that, now our app becomes interactive in under 4 seconds

- Serving compressed files 
with gzip or Brotli
Compressing files can help to reduce the downloaded resource sized. Depending on the file types, you can gain more than a 30% reduction.

- Compression has improved the performance a little bit more. But the more important gain for the compression is now with reduced file sizes from 4MB to 65KB from the initial state of the application, we are now in our performance budgets.

- Using HTTP/2 provides multiplexing 
and parallel requests

- Let's update our server to use ssl and http/2 protocol

- Again we can able to improve performance a little more.

- IE11 will officially end its life in August 2021
. Serving modern code for modern browsers

To support older browsers we need to add lots of polyfills in our code that increases the file sizes and effects the script execution times. However just serving modern code for modern browsers, our code doesn’t need transformations and browsers just execute it.

- Depending on your users, you can have closer to 100% modern browser usage in your application

- Let's update our browserslists settings to target modern browsers

- After starting to serve modern code, we can able to reach 55 in performance. However, there are still things to improve like our variable time to interactive duration and web vitals

- Serving web optimised images or videos
 will improve loading performance

Unoptimised images will lead to bigger file sizes that cause slow rendering images and page load

- For example, webp is a next-generation image file format that can provide more than 50% smaller file sizes than the classic jpg files

- …and lazy loading images will reduce network calls.

Off-screen images will not be downloaded unless they are available in the visible area of the page.

- By adding `loading=“lazy”` attribute to our image elements, we can tell browsers to lazy load them

- After using webp and lazy loading now we have performance over 60 and a good speed index and time to interactive

- Using proper size images 
will drastically improve the performance
For a small thumbnail image or banner, we do not need to load an UltraHD image. Having properly sized images will speed up da page load and reduce the download size that also helps your website bandwidth.

- It is amazing that we have almost the top score from performance and we have less than a 1-second loading app. Almost immediate.

- A service-worker file can run in the background,
helps to prefetch/preload next resources, can provide route based caching,
and network optimisation.

- Workbox can provide out of the box service worker creation, preload/prefetch resource definitions.

- With service-worker and additional manifest.json file, our app becomes a Progressive Web App, that can be installable, benefit the offline work capabilities and immediate load from home screens.

- And as the last step, let's add some 
API caching and offline support

- You can have various offline data caching strategies, but let's for the sake of simplicity, use local storage to check if the data is cached or not.

- In this last step, as you can see the film strip here, our application loads almost immediately, under half seconds.

- By using PWAs and optimisation techniques, 
we were able to improve our page loading time 
from more than 5 seconds to less than 0.5 s

Demo app: https://github.com/bcinarli/lightning-fast-loading
Fast Load Times: https://web.dev/fast/
Progressive Web Apps: https://web.dev/progressive-web-apps/

80d92d5d0bd0170aebf6e07589a0b571?s=128

Bilal Çınarlı

April 19, 2021
Tweet

Transcript

  1. Lightning Fast Web Apps

  2. Bilal Çınarlı Sr. Frontend Architect @Adidas @bcinarli

  3. How soon can you load a page? #iJScon @bcinarli

  4. How can you stay fast? #iJScon @bcinarli

  5. Nowadays, average time to interactive duration is almost 10 seconds

    #iJScon @bcinarli
  6. #iJScon @bcinarli 73 KB 1 MB 466 KB

  7. How can I check those for my website? #iJScon @bcinarli

  8. Lighthouse is the most simple one to use #iJScon @bcinarli

  9. A Journey to Loading Fast

  10. Let’s think we have an online shop #iJScon @bcinarli

  11. #iJScon @bcinarli

  12. #iJScon @bcinarli

  13. Minifying text based assets will reduce download and loading times.

    #iJScon @bcinarli
  14. optimization: { minimize: true, minimizer: ['...', new CssMinimizerPlugin()] } #iJScon

    @bcinarli
  15. #iJScon @bcinarli

  16. Splitting big single file to smaller easy to load chunks

    will easy code execution timing. #iJScon @bcinarli
  17. optimization: { minimize: true, minimizer: ['...', new CssMinimizerPlugin()], runtimeChunk: 'single',

    splitChunks: { cacheGroups: { react: { chunks: 'all', name: 'react', test: /(?<!node_modules.*)[\\/]node_modules[\\/](react|react-dom|scheduler|prop-types)[\\/]/, priority: 50, enforce: true }, vendors: { test: /[\\/]node_modules[\\/]/, chunks: 'initial', name: 'vendor', priority: 30, enforce: true, minChunks: 1, reuseExistingChunk: true } } } } #iJScon @bcinarli
  18. #iJScon @bcinarli

  19. Serving compressed files with gzip or Brotli #iJScon @bcinarli

  20. #iJScon @bcinarli

  21. Using HTTP/2 provides multiplexing and parallel requests #iJScon @bcinarli

  22. #iJScon @bcinarli

  23. #iJScon @bcinarli

  24. IE11 will officially end its life this August Serving modern

    code for modern browsers #iJScon @bcinarli
  25. #iJScon @bcinarli

  26. "browserslist":[ "IE 11", "> 0.5%", "last 1 version", "not dead"

    ] #iJScon @bcinarli "browserslist":[ "Chrome >= 61", "Edge >= 16", "Firefox >= 60", "Safari >= 10.1", "Opera >= 48" ]
  27. #iJScon @bcinarli

  28. Serving web optimised images or videos will improve loading performance

    #iJScon @bcinarli
  29. #iJScon @bcinarli

  30. …and lazy loading images will reduce network calls. #iJScon @bcinarli

  31. <img src={`${IMAGE_API}/next-gen/${nextgen}.webp`} alt={name} className={css('product-card-image-element')} loading="lazy" /> #iJScon @bcinarli

  32. #iJScon @bcinarli

  33. Using proper size images will drastically improve the performance #iJScon

    @bcinarli
  34. <img src={`${IMAGE_API}/next-gen-proper-sizes/${nextgen}.webp`} alt={name} className={css('product-card-image-element')} loading="lazy" /> #iJScon @bcinarli

  35. #iJScon @bcinarli

  36. A service-worker file can run in the background, helps to

    prefetch/preload next resources, can provide route based caching, and network optimisation. #iJScon @bcinarli
  37. new WorkboxPlugin.GenerateSW({ clientsClaim: true, skipWaiting: true, exclude: [/\.LICENSE\.txt?$/, /\.html?$/] }),

    #iJScon @bcinarli
  38. #iJScon @bcinarli

  39. And as a last step, lets add some API caching

    and offline support #iJScon @bcinarli
  40. const cachedProducts = localStorage.getItem('gadgets'); useEffect(() =>" { if (!cachedProducts) {

    (async () =>" { const response = await fetch(`${API}/gadgets`); const fetchedProducts = await response.json(); localStorage.setItem('gadgets', JSON.stringify(fetchedProducts)); setProducts(fetchedProducts); })(); } else { setProducts(JSON.parse(cachedProducts)); } }, []); #iJScon @bcinarli
  41. #iJScon @bcinarli

  42. By using PWAs and optimisation techniques, we were able to

    improve our page loading time from 5+ s to < 0.5 s #iJScon @bcinarli
  43. #iJScon @bcinarli

  44. • Demo app: https://github.com/bcinarli/lightning-fast- loading • Fast Load Times: https://web.dev/fast/

    • Progressive Web Apps: https://web.dev/progressive-web- apps/
  45. Thank you @bcinarli