Web Fundamentals: Critical Rendering Path
Goal: Prioritize and display the content the visitor wants to read first.
Also, making sure it bloody works! See my old hero Eric Meyer getting 'a bit antsy' about modern web sites.
Constructing the Object Model
Before the browser can render it needs to construct th DOM and CSSOM. It does it's processing in this order:
bytes-characters-tokens-nodes-object model
HTML => DOM, CSS => CSSOM
The DOM and CSSOM are independent of each other.
The DOM is the browser's representation of the HTML. The browser builds the HTML incrementally.
Render Tree Construction, Layout and Paint
DOM + CSSOM = Render Tree
The render tree only consists of items it requires, that is, it won't have items set to display:none
or the head
.
Layout computes exact pixel positions.
Paint is the last step.
Render Tree Construction Process:
- Traverse the DOM, check for visible nodes, check CSS for more nodes to be hidden.
- Apply CSS rules to visible nodes.
- Emit visible nodes (emit to the next step) NB:
visiblity:hidden
!=display:none
The browser is now ready to being the Layout process. (painting or 'rasterizing' follows after that)
NB: Always include:
<meta name="viewport" content="width=device-width">
to get accurate widths.
Modifying the DOM or CSS will restart the whole Render Tree construction, Layout and Paint process!
Render Blocking CSS
CSS is a render blocking resource, so:
- Make it lean
- Deliver it a quick as possible
- Use media types and queries
- Deliver the CSS file as early as possible.
Adding Interactivity with JavaScript
Note that JavaScript can manipulate CSS style, so the browser must wait for the CSSOM model to be built before it can process any JavaScript.
Make sure to async any JavaScript where possible to prevent it being Render Blocking.
Important points about JavaScript in the CRP:
- The location in the file (the "when") is very important.
- DOM construction is paused until a script is finished.
- JavaScript can query and modify the DOM and CSSOM
- JavaScript is delayed until the CSSOM is ready.
Optimizing the Critical Rendering Path is optimizing the relationship graph of CSS, HTML and JavaScript.
Measuring the CRP with Navigation Timing
The following time stamps tell us about the timing of key events:
- domLoading: browser ready to start parsing first received bytes of HTML
- domInteractive: (DOM ready) finished parsing all HTML and DOM construction complete
- domContentLoaded: (DOM & CSS ready) DOM ready and no stylesheets blocking JavaScript execution, ready to start making the render tree.
- domComplete: all downloaded, spinner has stopped spinning
- loadEvent: "onload" event gets fired
Analyzing CRP Performance
In the Network Profile in Chrome Dev Tools: the Blue Line = DOMContentLoaded, the Red Line = "onload" event.
Note: async JavaScript doesn't block the CRP and inlined CSS is faster.
Some CRP vocabulary:
- Critical Resource: resource that blocks page rendering
- Critical Path Length: number of round trips, or total time required to fetch all resources
- Critical Bytes: total byte required to get to first render, which is the sum of file sizes of all Critical Resources.
Optimizing the CRP
- Minimize the number of critical resources
- Minimize the number of critical bytes
- Minimize the critical path length
General sequence of steps to optimize CRP:
- Analyze the critical path: number of resources, bytes, length
- Minimize number of critical resources: eliminate, defer download, async
- Optimize order - critical assets: first and early
- Optimize size: reduce bytes in critical resources (and reduces round-trips.
PageSpeed Rules and Recommendations
- Eliminate Render blocking JavaScript and CSS
- Optimize JavaScript, use async if possible
- Optimize CSS usage, put CSS as early as possible, avoid CSS @import (extra round-trips on discovery) and consider inlining render blocking CSS.
Optimizing Performance
Eliminating Unnecessary Downloads
The fastest and most optimized resource, is a resource not sent.
- List and actively maintain a all your own and 3rd party resources.
- Measure performance of each.
- Decide if each is providing value
Optimizing Encoding and Transfer of textual assets
Data compression 101
- encoding data into fewer bits
- elimination is always better than compression
- many different tools and techniques
- a variety of tools and techniques will do the best job
Minification: Preprocessing & context specific optimizations
- comment stripping
- whitespace stripping
- code optimization like rule collapsing
All best done as part of a build / release cycle
Text Compression with Gzip
- Gzip performs best on text-based assets like JavaScript, CSS and HTML
- All browsers support it
- Server needs to be configured to enable GZip
- Some CDNs require special care
HTML5 Boilerplate project contains sample config files for all common servers. NB: really really small files, might actually grow with GZip.
Image Optimization
Eliminating and Replacing Images
- Eliminate unnecessary images
- Leverage CSS3 effects where possible (gradients, shadows, corner-radius)
- Use web-fonts instead of encoding text in images
Vector Vs Raster Images
- SVG is the vector based format supported by browsers
- GIF, PNG & JPG are the old raster formats, the new ones are JPEG-XR and WebP
- Vectors for geometric images, raster for photos, PNG for small color palettes, GIF for animations (almost never). JPEG for full on photos.
Implications of High Res Screens
HRSs have multiple device pixels per CSS pixel. High resolution images require significantly higher number of bytes per pixel. Image optimization techniques are the same regardless of resolution.
Fore high DPI, deliver multiple versions / variants of raster images (not so for vectors).
Vectors should be minified and Gzipped (svgo).
Lossy Vs Lossless Image compression
Every image will benefit differently from each different compressor, find the best one for each image.
Fomats
- gif, jpg and png: all browsers
- jpef-xr: internet explorer
- WebP: Chrome, Opera, Android
- Safari?
Tools
gifsicle, jpegtran, optipng (lossless), pngquant (lossy)
Delivering Scaled Image Assets
NB: Hovering over an image in Dev Tools will show the displayed and natural images dimensions. Resizing images takes processor time as well as showing unnecessary pixels that have shipped.
Summary for Image Optimization
- Prefer vectors
- Minify SVGs and GZip 'em too
- Pick the best raster format for the image
- Remove unnecessary meta data
- Serve scaled images
- Automate, automate, automate
Webfont Optimization
Webfont are selectable, zoomable, searchable, high DPI friendly, and can actually reduce loading times - if done correctly.
Formats:
- WOFF2 (WIP)
- WOFF (not on old browsers)
- *EOT (internet explorer only)
- *TTF (partial internet explorer support)
* need to be compressed
Serve WOFF2 when possible.
Defining Font-Family with @font-face
- use the 'woff2' hint to specify multiple variants
- order matters, as the browser will use the first one that it is able to use
- Avoid relying on font-synthesis
- Be aware of font ranges and internationalization
Paste links like this into the browser to get the full CSS for inlining into a CSS file back: fonts.googleapis.com/css?family=Pacifico
returns:
/* latin */ @font-face { font-family: 'Pacifico'; font-style: normal; font-weight: 400; src: local('Pacifico Regular'), local('Pacifico-Regular'), url(http://fonts.gstatic.com/s/pacifico/v7/Q_Z9mv4hySLTMoMjnk_rCXYhjbSpvc47ee6xR_80Hnw.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; }
Optimizing Loading and Rendering
- Font requests are delayed until the render tree is built
- Font loading API may let us override lazy font loading
- Font inlining allows us to override lazyload in old browsers
Painting of fonts is different in browsers and can cause blank text (temporarily) or font-repaints after it has downloaded.
Font loading API
Can tell the browser to fetch and load a font before it's too late. CSS inlining in CSS stylesheets overrides lazyload. Put the fonts in separate CSS file to prevent redownloads when regular CSS gets some changes.
Optimize font reuse with HTTP Caching
Give them a long max-age expiry. Specify both: conditional ETag header, and optimal Cache-Control policy (don't use localStorage).
Webfonts Summary Checklist
- Audit and Monitor Font use, use few fonts and few variants.
- Subset font resources, Unicode ranges.
- Deliver optimized formats (WOFF etc) and ZIP when possible.
- Specify revalidation and optimal caching policies
- Use Font Loading API and optimize the CRP
HTTP Caching
The ability to cache and reuse previously fetched resources is critical.
Validating Cached Responses with ETags
- Validation token is emitted by the servers as ETag HTTP Header
- Tokens enable efficient update checks, no transfer if resource has not changed.
Client sends the next requests with if-none-match:
headers, if resource has not changed it responds with 304 Not Modified
.
The browser and server do all this work, we just need to ensure the server is properly set up (HTML5 Boilerplate) and is in fact, providing theETag tokens.
Cache-Control
- Each resource can define its own caching policy via Cache-Control HTTP header.
- Cache-Control directives control who can cache the response, under which conditions, and for how long.
The best request doesn't go to the server, a local copy can avoid this.
"no-cache" forces the client to always check for a change first before going to the local copy, if allowed to.
"no-store" forces re-download every time (good for banking and things like that).
"Public" and "Private"
Public - can be cached
Private - intended for a single user, not allowed to be cached by an intermediate like a CDN.
"max-age"
Max time in seconds that the fetched response is allowed to be used e.g. "max-age - 60" means the response can be cached and reused for the next 60 seconds.
Ideally you should try to Cache as many responses as possible for as long as possible, and provide validation tokens for each response.
Summary
Audit your pages to identify which resources can be cached and for how long, and ensure they are returning appropriate Cache-Control and ETag headers.
Invalidating and Updating Cached Responses
- Locally cached responses are used until the resource expires
- Embedding a file content fingerprint in the URL enables us to force the client to update to a new version for the response
- Each application needs to define its own cache hierarchy for optimal performance.
We can't force an update to a cached resource without changing its URL. This is done by embedding a version number or date in the file name.
Caching Checklist
- Use consistent URLs to allow things to get cached
- Ensure server is providing valid ETags
- Identify which resources can be cached by intermediaries like CDNs
- Determine optimal cache lifetime for each resource
- Determine best cache hierarchy, HTML -> "no-cache", fingerprinted UTLs etc.
- Minimize "churn" files that get updated frequently might be delivered separately like or less frequently like CSS with @font-face definitions.
Rendering Performance
Pages should be as smooth as butter and stick to the finger like glue.
60FPS: Most devices refresh the screen 60 times per second. That is 16.66 milliseconds per frame, which should be the target. The best practice target is 10 ms.
The Pixel Pipeline
JS -> Style -> Layout -> Paint -> Composite
PERFORMANCE is the Art of Avoiding Work, and making work that you have to do efficient.
Some of the Types of recalculations that can be costly:
- reflow => repaint, composite
- repaint => composite
- composite* (animations or scrolling)
Optimize JavaScript Execution
Badly timed or long-running JS can cause bottlenecks.
- use requestAnimationFrame()
- move long-running JS off the main thread to Web Workers
- Use micro-tasks to make DOM changes over several frames
- Use Dev Tools Timeline & Profiler to make assessments
NB: The JavaScript you write looks nothing like the JavaScript that gets executed.
Scrolling Animation Target: 3-4 ms
Web Workers and task-splitting let you keep the main thread active while processing data (for example) in the background (use a spinner or progress bar to tell the user it is working).
Beware: of micro-optimizations that don't actually help a lot.
Reduce Speed and Complexity of Style Calculations
Changing the DOM by adding, removing, attributes, classes, animations, all recalculate the Render Tree with reflow etc.
- Reduce the complexity of selectors, maybe use BEM (bem.info)
- Reduce number of elements on which style calculations must be computed on.
Avoid Large, Complex Layouts and Layout Thrashing
- Layout is scoped to the whole document
- Avoid triggering it as much as possible
- Assess layout model performance, new Flexbox is faster than older Flexbox
- Avoid forced synchronous layouts and thrashing; read style values and then make changes.
The 2 Biggest Concerns / Factors
- The Number of Elements using Layout
- The Complexity of the Layouts
FastDOM automatically batches reads and writes for layout, and prevents triggering forced synchronous layouts.
Simplify Paint Complexity And Reduce Paint Areas
Painting is often the biggest delay of all, avoid it if at all possible.
- Everything except transforms and opacity trigger paint
- Paint is the most expensive part of the pixel pipeline - AVOID
- Reduce paint areas through layer promotion, and orchestration of animations
- Use Dev Tools paint profiler to assess what's going on (show paint rectangles)
In sum: Layout -> Always triggers paint
NB: If using paint in Profile only use Paint, because it has a significant profiler performance hit.
Promote Elements to Move or Fade
Make use of the layer compositing
Using the CSS property will-change: transform;
will effectively make the element a layer
For browsers like mobile Safari (mis)use a 3D transform: translate: translateZ(0);
NB: Don't create too many layers as they all require separate memory management. Beware: Don't promote elements without profiling to prove performance improvement.
- Browsers Union paint areas, meaning a paint at top and bottom will repaint the whole page.
- Try to keep transitions and animations from overlapping
- Shadows and blurs are really expensive
Avoid paint during animations in particular, as the 10 ms you have is not usually enough, especially on mobile.
Stick to Composite-Only Properties and Manage Layer Content
- Stick to transform and opacity changes for animation
- Promote moving elements with will-change or translateZ
- Avoid over-promoting
- POSITION transform: translate(Npx, Npx);
- SCALE transform: scale(N);
- ROTATION transform: rotate(Ndeg);
- SKEW transform: skew(X|Y)(Ndeg);
- MATRIX transform: matrix(3d)(...);
- OPACITY: 0 ... 1;
NB: Also look at FLIP animation principle from Google and Udacity
Dev Tools > Profiler > Paint. Can show what layers are being used and how many.
Debounce Your Input Handlers (WTF?)
Input handlers can cause additional layout and block frames from completing.
- Avoid long-running handlers, they can block scrolling
- Do not make style changes in handlers
- Debounce handlers, store event values and deal with style changes in the next requestAnimationFrame callback.