/uxm

A utility library for collecting web performance metrics that affect user experience.

Primary LanguageJavaScriptMIT LicenseMIT

A utility library for collecting web performance metrics
that affect user experience.

Why?UsageAPICredits



Modern web platform provides a lot of APIs to analyze page speed information. But it's hard to follow them and even harder to deal with the lack of implementation in different browsers.

UXM is a modular library that allows to combine various functions and collect the data you need. Think about it, as Lodash for user experience APIs.

Use cases:

  • Collect RUM data.
  • Build private version of Chrome User Experience Report.
  • Audit the page performance using Puppeteer (example).
  • Dynamically evaluate the performance of the user's device and adapt the UI.

Features:

Usage

Install using yarn/npm:

yarn add uxm
npm i -S uxm

Import uxm and call it at the end of the page loading to collect CrUX-like data:

import { uxm } from 'uxm'

uxm().then(metrics => {  
  console.log(metrics) // ->
  {
    "deviceType": "desktop",
    "effectiveConnectionType": "4g",
    "firstPaint": 1646,
    "firstContentfulPaint": 1646,
    "domContentLoaded": 1698,
    "onLoad": 2508
  }
})

Or collect 2 performance metrics associated with URL:

import { getUrl, getFirstContentfulPaint, getDomContentLoaded } from 'uxm'

const metrics = {
  url: getUrl(),
  fcp: getFirstContentfulPaint(),
  dcl: getDomContentLoaded()
}

Or analyze current device and connection:

import { getDeviceType, getDeviceMemory, getEffectiveConnectionType } from 'uxm'

const device = {
  type: getDeviceType(),
  memory: getDeviceMemory(),
  connection: getEffectiveConnectionType()
}

API

An API is a set of pure functions with one exception to uxm, which is a meta-function to collect multiple metrics at once.

uxm(opts = {})

Returns a Promise that resolves after load event fired. A default set of metrics is defined by Chrome User Experience Report, but you can customize them using options (url, userAgent, deviceMemory, userTiming, longTasks, resources).

Or pass all to get the full report:

import { uxm } from 'uxm'

uxm({ all: true }).then(metrics => {  
  console.log(metrics) // ->
  {
    "deviceType": "phone",
    "effectiveConnectionType": "4g",
    "firstPaint": 531,
    "firstContentfulPaint": 531,
    "domContentLoaded": 768,
    "onLoad": 1317,
    "url": "https://www.booking.com/",
    "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1",
    "deviceMemory": "full",
    "userTiming": [
      {
        "type": "measure",
        "name": "b-stylesheets",
        "startTime": 0,
        "duration": 436
      },
      ...
    ],
    "longTasks": [
      {
        "startTime": 587,
        "duration": 79
      },
      ...
    ],
    "resources": [
      {
        "url": "https://booking.com/",
        "type": "navigation",
        "size": 77953,
        "startTime": 0,
        "duration": 1568
      },
      ...
    ]
  }
})

mark(markName)

Create User Timing mark to mark important loading event. A convenient shortcut for window.performance.mark.

import { mark } from 'uxm'

mark('page load started')
// ...
mark('hero image displayed')
// ...
mark('page fully loaded')

measure(measureName, [startMarkName])

Create User Timing measure to evaluate timing between 2 marks. A convenient shortcut for window.performance.measure.

import { mark, measure } from 'uxm'

mark('start load fonts')
// ...
measure('fonts loaded', 'start load fonts')

getUserTiming()

It returns an array with collected performance marks/measures. Each item contains:

  • type - "mark" or "measure"
  • name - unique name
  • startTime - start time since page load
  • duration - measure duration

Example:

[
  {
    "type": "mark",
    "name": "boot",
    "startTime": 1958
  },
  {
    "type": "measure",
    "name": "page did mount",
    "startTime": 1958,
    "duration": 197
  }
]

getFirstContentfulPaint()

It returns the time when first paint which includes text, image (including background images), non-white canvas, or SVG happened. W3C draft for Paint Timing 1.

getFirstPaint()

It's similar to getFirstContentfulPaint but may contain a different value when First Paint is just background change without content.

getDomContentLoaded()

It returns the time when DOMContentLoaded event was fired.

getOnLoad()

It returns the time when load event was fired.

getEffectiveConnectionType()

It returns the effective connection type (“slow-2g”, “2g”, “3g”, or “4g”) string as determined by round-trip and bandwidth values. W3C draft for Network Information API.

getDeviceType()

It returns the device type ("phone", "tablet", or "desktop") string using the lightweight heavy-tested user-agent parser.

getDeviceMemory()

It returns the device memory ("full" or "lite") string, depends if available memory is bigger than 1 GB. Learn more about Device Memory.

getUrl()

It returns a current page URL. A convenient shortcut for window.location.href.

getUserAgent()

It returns a User-Agent string. A convenient shortcut for window.navigator.userAgent.

getResources()

It returns an array of performance information for each resource on the page. Each item contains:

  • url - resource URL
  • type - one of resource types ("navigation", "link", "img", "script", "xmlhttprequest", or "font")
  • size - transferred size in bytes
  • startTime - when load started
  • duration - loading time in milliseconds

Example:

[
  {
    "url": "https://booking.com/",
    "type": "navigation",
    "size": 79263,
    "startTime": 0,
    "duration": 1821
  },
  {
    "url": "https://q-fa.bstatic.com/mobile/css/core_not_critical_fastly.iq_ltr/8051b1d9fafb2e6339aea397447edfded9320dbb.css",
    "type": "link",
    "size": 54112,
    "startTime": 515,
    "duration": 183
  },
  {
    "url": "https://r-fa.bstatic.com/mobile/images/hotelMarkerImgLoader/211f81a092a43bf96fc2a7b1dff37e5bc08fbbbf.gif",
    "type": "img",
    "size": 2295,
    "startTime": 657,
    "duration": 181
  },
  {
    "url": "https://r-fa.bstatic.com/static/js/error_catcher_bec_fastly/ba8921972cc55fbf270bafe168450dd34597d5a1.js",
    "type": "script",
    "size": 2495,
    "startTime": 821,
    "duration": 43
  },
  ...
]

getLongTasks()

It returns an array of { startTime, duration } pairs. Until buffered flag supported, you need to add extra script to the <head /> to collect all Long Tasks:

<script>
!function(){if('PerformanceLongTaskTiming' in window){var g=window.__lt={e:[]};
g.o=new PerformanceObserver(function(l){g.e=g.e.concat(l.getEntries())});
g.o.observe({entryTypes:['longtask']})}}();
</script>

And then get them using:

import { getLongTasks } from 'uxm'
getLongTasks() // [{"startTime": 672, "duration": 84}, {"startTime": 931, "duration": 84}, {"startTime": 1137, "duration": 135}]

Learn more about Long Tasks.

Credits

Sponsored by Treo.sh - Page speed monitoring made easy.