How to Make Your Site Faster with the Performance API

How to Make Your Site Faster with the Performance API

This tutorial explains how to use the Performance API to record DevTool-like statistics from real users accessing your application.

Assessing web application performance using browser DevTools is useful, but it’s not easy to replicate real-world usage. People in different locations using different devices, browsers, and networks will all have differing experiences.

An Introduction to the Performance API

The Performance API uses a buffer to record DevTool-like metrics in object properties at certain points in the lifetime of your web page. Those points include:

  1. Page navigation: record page load redirects, connections, handshakes, DOM events, and more.
  2. Resource loading: record asset loading such as images, CSS, scripts, and Ajax calls.
  3. Paint metrics: record browser rendering information.
  4. Custom performance: record arbitrary application processing times to find slow functions.

All the APIs are available in client-side JavaScript, including Web Workers. You can detect API support using:

if ('performance' in window) { // call Performance APIs } 

Note: be aware that Safari doesn’t support all methods, despite implementing most of the API.

The custom (user) performance APIs are also replicated in:

  • the Node.js built-in performance_hook module, and
  • the Deno performance API (scripts using it must be run with the --allow-hrtime permission).

Isn’t Date() Good Enough?

You may have seen examples using the Date() function to record elapsed times. For example:

const start = new Date(); // ... run code ... const elapsed = new Date() - start; 

However, Date() calculations are limited to the closest millisecond and based on the system time, which can be updated by the OS at any point.

The Performance API uses a separate, higher-resolution timer that can record in fractions of a millisecond. It also offers metrics that would be impossible to record otherwise, such as redirect and DNS lookup timings.

Recording Performance Metrics

Calculating performance metrics in client-side code is useful if you can record it somewhere. You can send statistics to your server for analysis using Ajax Fetch / XMLHttpRequest requests or the Beacon API.

Alternatively, most analytic systems offer custom event-like APIs to record timings. For example, the Google Analytics User Timings API can record the time to DOMContentLoaded by passing a category ('pageload'), variable name ("DOMready"), and a value:

const pageload = performance.getEntriesByType( 'navigation' )[0]; ga('send', 'timing', 'pageload', 'DOMready', pageload.domContentLoadedEventStart); 

This example uses the Page Navigation Timing API. so let’s start there …

Testing your site on a fast connection is unlikely to be indicative of user experience. The browser DevTools Network tab allows you to throttle speeds, but it can’t emulate poor or intermittent 3G signals.

The Navigation Timing API pushes a single PerformanceNavigationTiming object to the performance buffer. It contains information about redirects, load times, file sizes, DOM events, and so on, observed by a real user.

Access the object by running:

const pagePerf = performance.getEntriesByType( 'navigation' ); 

Or access it by passing the page URL (window.location) to the getEntriesByName() method:

const pagePerf = performance.getEntriesByName( window.location ); 

Both return an array with a single element containing an object with read-only properties. For example:

[ { name: "", initiatorType: "navigation", entryType: "navigation", initiatorType: "navigation", type: "navigate", nextHopProtocol: "h2", startTime: 0 ... } ] 

The object includes resource identification properties:

namethe resource URL
entryTypeperformance type — "navigation" for a page, "resource" for an asset
initiatorTyperesource which initiated the download — "navigation" for a page
nextHopProtocolnetwork protocol
serverTimingarray of PerformanceServerTiming objects

Note: performanceServerTiming name, description, and duration metrics are written to the HTTP Server-Timing header by the server response.

The object includes resource timing properties in milliseconds relative to the start of the page load. Timings would normally be expected in this order:

startTimetimestamp when fetch started — 0 for a page
workerStarttimestamp before starting the Service Worker
redirectStarttimestamp of the first redirect
redirectEndtimestamp after receiving the last byte of the last redirect
fetchStarttimestamp before the resource fetch
domainLookupStarttimestamp before the DNS lookup
domainLookupEndtimestamp after the DNS lookup
connectStarttimestamp before establishing a server connection
connectEndtimestamp after establishing a server connection
secureConnectionStarttimestamp before the SSL handshake
requestStarttimestamp before the browser request
responseStarttimestamp when the browser receives the first byte of data
responseEndtimestamp after receiving the last byte of data
durationthe time elapsed between startTime and responseEnd

The object includes download size properties in bytes:

transferSizethe resource size, including the header and body
encodedBodySizethe resource body size before decompressing
decodedBodySizethe resource body size after decompressing

Finally, the object includes further navigation and DOM event properties (not available in Safari):

typeeither "navigate", "reload", "back_forward" or "prerender"
redirectCountnumber of redirects
unloadEventStarttimestamp before the unload event of the previous document
unloadEventEndtimestamp after the unload event of the previous document
domInteractivetimestamp when HTML parsing and DOM construction is complete
domContentLoadedEventStarttimestamp before running DOMContentLoaded event handlers
domContentLoadedEventEndtimestamp after running DOMContentLoaded event handlers
domCompletetimestamp when DOM construction and DOMContentLoaded events have completed
loadEventStarttimestamp before the page load event has fired
loadEventEndtimestamp after the page load event. All assets are downloaded

Example to record page loading metrics after the page has fully loaded:

'performance' in window && window.addEventListener('load', () => { const pagePerf = performance.getEntriesByName( window.location )[0], pageDownload = pagePerf.duration, pageDomComplete = pagePerf.domComplete; }); 

Continue reading How to Make Your Site Faster with the Performance API on SitePoint.