Last month (May 2020) I read this book on web performance by Maximiliano Firtman. I'll give you a short summary of the key takeaways of the book and what I think of it as part of my review.
I also added my personal notes that I took while reading the book. These are bullet points on what I found interesting and they aren't always self-explanatory. That's why I recommend that — if you find the topics mentioned interesting — you get a copy of the book here (affiliate link*) and support the author.
The idea of sharing the notes I took came from a video about note taking by Thomas Frank.
The author also published a newer book — Hacking Web Performance (2018) — which you can read for free here. I just found out about this book after finishing this one though and I haven't read it yet.
Thoughts
I've been researching the internet about performance metrics and how to improve the performance of webpages for quite some time before reading this book. That's why I recognized many things mentioned in this book already. Nevertheless, it made my understanding of the topic more complete by connecting the dots and adding valuable insights and ideas on how to improve performance.
Reading this book motivated me to implement some of the optimizations suggested in my own work and projects and I'll certainly take it as a reference in the future when doing performance optimizations as well.
What I find interesting is that the author's personal webpage is slow.
Key takeaways
The book guides you through the whole process of optimizing performance. It starts by exploring the mobile landscape and continues explaining the most important metrics and tools you can use to measure performance. After that, the author explains how to optimize performance — first for the first visit and then for repeat visits and mobile devices.
My main takeaways are:
- (High) performance is important — especially on mobile devices -> every millisecond counts
- Before improving performance, measure
- Optimize for the first visit, after the first load and responsiveness
- Lots of ideas for improving performance
Book notes
These notes are by no means a comprehensive summary of each chapter but rather notes on what I found interesting or useful.
- Chapter 1: The mobile web world
- Chapter 2: Where to measure performance
- Chapter 3: Web performance basics
- Chapter 4: Measurement tools
- Chapter 5: Performance APIs
- Chapter 6: Optimizing for performance
- Chapter 7: Optimizing after first load
- Chapter 8: Optimizing responsiveness and the post-loading experience
- Chapter 9: Responsive web design
- Chapter 10: Extreme mobile web performance
- Chapter 12: Mobile web performance checklist
Chapter 1: The mobile web world
- The author goes over the current state of technology at the time of writing the book (2016).
- This includes:
- Mobile browsers/ mobile web platforms/ web apps and their differences
- Comparing different rendering engines and JavaScript execution engines
- Mobile devices available (hardware)
- Networks
- Operating systems
- Rendering engines (or layout engines)
- In charge of parsing, downloading and rendering HTML, CSS, images, svgs, etc.
- Most wide spread engines are WebKit, Blink (from Google), Trident (IE), Gecko, Presto (Opera, now using Blink).
- Execution engines - In charge of executing JavaScript code
- Classic engines -> interpret JS while executing
- JIT engines (Just In Time) -> precompile JS (just) before it's being executed
Some browsers, such as Opera or Android browser, are not relevant anymore due to their low marketshare.
Chapter 2: Where to measure performance
- Emulators
- Simulate hardware and software
- Device doesn't know it's not a real phone
- Simulators
- Simulate only parts of the real device
- Not suited for performance testing
- Testing on real devices
- Better feeling of performance and UX
- Open device labs are places where you can go and test you site on real devices for free (all over the world)
- Cloud based testing is a good alternative to open device labs
- Desktop simulators
- Allow you to throttle network / CPU
- On-device connection simulators
- Connect to a real device through a desktop computer
- Lets you simulate a 3G connection on a real device (example is using iOS)
Chapter 3: Web performance basics
I found this chapter especially useful since it sheds some light on the confusing metric jungle.
Loading measurement basics
Waterfall chart
- Always compare results of first view (empty cache) and repeat view (cached).
- Resource timing - stages each resource goes through and of which the loading time is composed
- 1 . DNS lookup
- 2 . Initial connection (TCP handshake)
- 3 . Time to First Byte (TTFB - Time the server takes to respond)
- 4 . Download
- These steps are usually shown in all waterfall charts in different colors
- Blocking resources won't let other resources being downloaded while they are being parsed.
- Can be seen as a gap in the waterfall (this is the time it takes to execute the JavaScript code of that resource).
- Critical path - Link to recommended article from the book here.
- Resources that define the minimal load time
- Should be optimized
- Milestones
- Start render - first thing the user sees after a blank page
- DOMContentLoaded
- Above the Fold Renderer - Everything within the viewport is rendered (depends on the screen size)
- Load time
- When DOM onload event has fired
- All initial resources have been loaded
The HAR format (HTTP Archive Format)
- Used for importing and exporting performance data
- Can be exported from the browser
Speed index
- Measures how much blank space the user is seeing
- Depends on the screen size (above the fold)
What to measure
- User's perception is more important than load-time
- Usually we want:
- A low speed index (<1500)
- Show content asap (less than 1 sec after the initial request)
- Small waiting areas (TTFB)
- Reduce final onload + start rendering asap
- Optimize the critical path
Custom metrics
- Metrics that have a meaning to you (like Twitter's Time To First Tweet)
- Time to First Meaningful Interaction (Google)
The RAIL approach (methodology by Google)
- Response-Animation-Idle-Load
- Depending on the context, different parts are prioritized
- Values we should aim for are:
- Response: 100ms = immediate (on user input for example)
- Animation: one frame every 16ms for 60FPS
- Idle: 50ms - every non-critical operation should take less than that to keep a good perception
- Load: 1s to load the initial content
Responsiveness measurement basics
- Responsiveness depends on harware, engines, screens
- Measurements involve:
- Feedback lag (like ontouch)
- Frame rate (while scrolling or animating)
- Network time for on-demand resources (XHR) -Things that affect responsiveness that are out of our control are:
- Video acceleration and GPU memory
- CPUs
- JS runtime (JIT or not)
- RAM
- Frames per second
- Persistent 30 fps perceived as faster than 60 fps that sometimes drop to 30
- Profiling charts
- Typically most interesting is CPU and memory
- Profiling affects performance
Chapter 4: Measurement tools
- Remote inspectors (for real devices)
- Available for Safari and Chrome
- Chrome rendering settings (for performance measurements)
- Show FPS meter -> directly see it on the device
- Continuous painting mode -> find out if content takes too much time to render
- Show paint rectangles / Show composite layout borders
- How is the browser rendering
- Show potential scrolling bottlenecks
- Proxy for devices that don't support remote inspectors
- Only able to measure network requests (Waterfall)
- Online tools
- Webpagetest
- Pagespeedinsights (lighthouse)
- Monitoring services
- New relic, Neustar, Keynote, Pingdom, etc.
Chapter 5: Performance APIs
- Navigation Timing APIs (navigationStart, domInteractive, etc.)
- Whole list in the book, explaining each of the available APIs
- can be used for analytics or to make live decisions on the fly
- timing.js -> script to get even more timings
- Resource timing APIs -> list in the book
- Network APIs
- Online event -> not reliable, use offline.js or check with own server to check if network is available
- Network information -> bandwidth, type (Wifi, etc.)
- Only available in Chrome
- Could be used to decide if HD or SD videos should be delivered
- Metered -> connection might be limited by ISP
- Don't rely on Wifi if you want to check if the user has a fast internet connection
- Wifi could be available on a bus through a 2G connection
- Beacon API
- Can be used to send data when the page is left
document.onunload
-> navigator.sendBeacon- Doesn't harm performance or delays the load process of the next page
window.onunload = () => {
navigator.sendBeacon('http://target-url.com/on-unload', data);
};
HTTP Client Hints
- Provides additional info about the device/browser in the HTTP header
UI APIs
- Page visibility (document.hidden)
- If page is being viewed or not (tab active)
- requestAnimationFrame
- For consistent animations
- requestIdle
- Shouldn't take more than 50ms
- Page visibility (document.hidden)
User timings
- performance.mark (I wrote an article about this here)
Frame timings
- setImmediate (better than setTimeout(fn, 0))
Chapter 6: Optimizing for performance
HTTP
- Enable keep alive on the server
- Support HTTP2
- Compression
- Gzip most common
- Zopfli -> for static sites
- Brotli -> alternative to Gzip, 25% greater compression
- BDHC -> Shared dictionary -> warms up the cache by analyzing traffic
- Don't compress already compressed files and formats (like most images)
- Avoid redirects
- HSTS - Strict Transport Policy
- Forces SSL (Redirect)
- Website can be whitelisted as HTTPS in some browsers (link here)
- Don't use app banners for the landing page (this can result in a 69% bounce rate)
- Show them after a few page visits
- Use native platform tools (iOS, Android)
Reducing requests
- Especially useful for HTTP 1.1 (v2 is using compression and allows more simultaneous requests)
- Domain Sharding
- HTTP limits the amount of requests only per host
- Splitting the request up into different subdomains will trick the browser into requesting more resources in parallel
- Not so usefult for HTTP2
- Cookieless Domain
- Cookies are sent by default with every request
- Set up a different domain for resources which don't need the cookie data
- Separate API from static files to take the load off the server
- Flush the HTML early
- Use DNS prefetching
CSS
<link>
tags block the rendering of everything below it- Webfonts -> If a CDN is used, the user might already have it in the browser cache
JavaScript
- Critical JS for rendering the first visit should be inlined (
<script>
tag) - The rest before the closing tag of the bode (
</body>
) - Images should fit the dimension they are displayed with
- There are frameworks for Server Side Rendering (SSR) only on the first visit (React, Angular, etc.)
Chapter 7: Optimizing after first load
- Prefetching
- There are different approaches, from browser implementations with HTML or HTTP headers to prefetching with JS
- Cache: Data can be stored in browser- or custom cache
- Application cache
- Prior to service workers
- Not so interesting anymore since service workers are supported by all modern browsers
- Service Worker
- Core technology of PWAs (Progressive Web Apps)
- Provides a cache storage API
- Cache is not automatically deleted when Service Worker is updated
- Background Sync API
- Background Periodic Sync API
Chapter 8: Optimizing responsiveness and the post-loading experience
<meta name="viewport" content="width=device-width, initial-scale=1">
Mobile browsers sometimes add a 300ms delay to each touch/click (waiting for double-tap-to-zoom)
- Solved by mobile optimized viewport
JavaScript
- Long operations in event handlers affect responsivenes and frame rate
- Deffering helps —
setTimeout
,setImmediate
,requestIdleCallback
- JS should be split up into smaller chunks to avoid blocking the DOM for too much time
- Use debouncing and throttling on event handlers and callbacks (like scrolling)
- Otherwise the event listener of the scroll event will be executed hundreds of times while scrolling
- Limits it to every x ms
- If 60fps are required a canvas might be a good solution
- Recommended library: react-canvas
- Use passive event listeners where the default can't be prevented
- Web workers can be used for long operations
- They execute the JS in another thread
- Frame rate
- Chrome has a paint flashin option that shows when something is rerenderd
- Avoid executing JS while scrolling (some browsers even block JS during scrolling for you)
- Add momentum to
overflow: scroll
-webkit-overflow-scrolling: touch
allows the browser to optimize this component
- CSS containment
- Changing elements with JS can trigger other elements to repaint due to CSS rules
- Such CSS rules are sibling selectors, descendant selectors, "nth-child", ...
- Use CSS
contain
(e.g.contain: size
) to prevent elements from repainting when the layout changes
- Network communication
- Server-sent events -> like websockets but unidirectional (server -> client)
Chapter 9: Responsive web design
- Not only the UI, but also performance should be different on mobile devices.
- Avoid using one HTML file for all device (same goes for CSS, images, other resources)
- Use conditional loading for resources
- Don't rely on media queries -> Files can be downloaded by the browser anyway
- Use JS's
matchMedia
query on devices whose context will not change (iPad won't suddenly change to a desktop) - Alternatively, there are libraries for this like Enquire.js
- Resize from big to small
- Phones don't resize to iPads
- Different HTML for phones (based on screen)
- Desktop HTML UI should still be responsive
Chapter 10: Extreme mobile web performance
- In this chapter the author shows some extreme measurements that can be taken to gain the best performance possible.
- The goal is to load all the data necessary for the first page visit within one single TCP package
- This means that in 14.6KB we need to include everything necessary
- Only load things required above the fold (ATF)
- Embed images as data URIs
- Use viewport of phablet for development as a reference
- CSS-extraction tools give you the code of the viewport (See "Critical" on Github)
- Lazy load the rest
- Images
- Client hints (CH) in the HTTP header tell the server which images are supported
- Non-blocking image decoding
- Decode image in web-worker
- Serve low-res images first and replace them later on (blurry photos like on medium)
- JavaScript
- Should be avoided first
- No big framework (> 10KB)
- Use server side rendering first
- SD/HD pattern -> depending on the client hints, offer a lower resolution of media resources
- PRPL Pattern - Push-Render-Precache-Lazy Load
- Future (or present now): WebAssembly, WebStreams
Chapter 12: Mobile web performance checklist
This chaper lists all the metrics and optimizations seen in the chapters before.
*As an Amazon Associate I earn from qualifying purchases.