Skip to content

Instantly share code, notes, and snippets.

@ms-fadaei
Created June 22, 2021 13:10
Show Gist options
  • Save ms-fadaei/d2645b461f008dd96036165e0433e454 to your computer and use it in GitHub Desktop.
Save ms-fadaei/d2645b461f008dd96036165e0433e454 to your computer and use it in GitHub Desktop.
Debug TTFB Like Google
/*
//
// With this file, you can debug the TTFB
// and figure out what's going on
//
// Install dependecies: npm i web-vitals
//
// Use it like this:
// Import {getRDT, getBC, getDNS, getHS, getREQ} from "./debug-ttfb.js";
// getDNS(console.log); // see: https://github.com/GoogleChrome/web-vitals#usage
//
// getRDT: Redirects duration
// getBC: Unload event, redirects and app cache duration
// getDNS: DNS lookup duration
// getHS: Initial connetctio duration (TCP + SSH)
// getREQ: The time between request start and respons start (Time To First Byte)
//
// See this image for more detail: https://w3c.github.io/navigation-timing/timestamp-diagram.svg
//
*/
// please intall web-vital package from npm
import {initMetric} from "web-vitals/dist/modules/lib/initMetric";
const afterLoad = (callback) => {
if (document.readyState === "complete") {
// Queue a task so the callback runs after `loadEventEnd`.
setTimeout(callback, 0);
} else {
// Use `pageshow` so the callback runs after `loadEventEnd`.
addEventListener("pageshow", callback);
}
};
const getNavigationEntryFromPerformanceTiming = () => {
// Really annoying that TypeScript errors when using `PerformanceTiming`.
const timing = performance.timing;
const navigationEntry = {
entryType: "navigation",
startTime: 0,
};
for (const key in timing) {
if (key !== "navigationStart" && key !== "toJSON") {
navigationEntry[key] = Math.max(timing[key] - timing.navigationStart, 0);
}
}
return navigationEntry;
};
export const getRDT = (onReport) => {
const metric = initMetric("RDT");
afterLoad(() => {
try {
// Use the NavigationTiming L2 entry if available.
const navigationEntry =
performance.getEntriesByType("navigation")[0] || getNavigationEntryFromPerformanceTiming();
metric.value = navigationEntry.redirectEnd - navigationEntry.redirectStart;
metric.delta = metric.value;
// In some cases the value reported is negative. Ignore these cases:
if (metric.value < 0) return;
metric.entries = [navigationEntry];
onReport(metric);
} catch (error) {
// Do nothing.
}
});
};
export const getBC = (onReport) => {
const metric = initMetric("BC");
afterLoad(() => {
try {
// Use the NavigationTiming L2 entry if available.
const navigationEntry =
performance.getEntriesByType("navigation")[0] || getNavigationEntryFromPerformanceTiming();
metric.value = navigationEntry.domainLookupStart;
metric.delta = metric.value;
// In some cases the value reported is negative. Ignore these cases:
if (metric.value < 0) return;
metric.entries = [navigationEntry];
onReport(metric);
} catch (error) {
// Do nothing.
}
});
};
export const getDNS = (onReport) => {
const metric = initMetric("DNS");
afterLoad(() => {
try {
// Use the NavigationTiming L2 entry if available.
const navigationEntry =
performance.getEntriesByType("navigation")[0] || getNavigationEntryFromPerformanceTiming();
metric.value = navigationEntry.domainLookupEnd - navigationEntry.domainLookupStart;
metric.delta = metric.value;
// In some cases the value reported is negative. Ignore these cases:
if (metric.value < 0) return;
metric.entries = [navigationEntry];
onReport(metric);
} catch (error) {
// Do nothing.
}
});
};
export const getHS = (onReport) => {
const metric = initMetric("HS");
afterLoad(() => {
try {
// Use the NavigationTiming L2 entry if available.
const navigationEntry =
performance.getEntriesByType("navigation")[0] || getNavigationEntryFromPerformanceTiming();
metric.value = navigationEntry.connectEnd - navigationEntry.connectStart;
metric.delta = metric.value;
// In some cases the value reported is negative. Ignore these cases:
if (metric.value < 0) return;
metric.entries = [navigationEntry];
onReport(metric);
} catch (error) {
// Do nothing.
}
});
};
export const getREQ = (onReport) => {
const metric = initMetric("REQ");
afterLoad(() => {
try {
// Use the NavigationTiming L2 entry if available.
const navigationEntry =
performance.getEntriesByType("navigation")[0] || getNavigationEntryFromPerformanceTiming();
metric.value = navigationEntry.responseStart - navigationEntry.requestStart;
metric.delta = metric.value;
// In some cases the value reported is negative. Ignore these cases:
if (metric.value < 0) return;
metric.entries = [navigationEntry];
onReport(metric);
} catch (error) {
// Do nothing.
}
});
};
export default {
getRDT,
getBC,
getDNS,
getHS,
getREQ,
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment