diff --git a/README.md b/README.md index 595fa33..f761262 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,35 @@ import { navigation, paint } from 'page-timing'; ## More functions +### `cachehit` +Get resource browser cache cachehit (0 - 1 number) +```js +import { cachehit } from 'page-timing'; + +const rate = await cachehit(); // 0.855 +``` + +Pass in a filter function to get results for a subset of resources: +```js +// Javascript files only +const javascriptCacheHitRate = await cachehit({ + filter: ({ name }) => /.m?js[$\?]/.test(name), + limit: 50 +}); + +// Specific domain +const myCDNCacheHitRate = await cachehit({ + filter: ({ name }) => /https:\/\/[\w-]*.mycdn.com/.test(), + limit: 50 +}); +``` + +#### Arguments +- **filter**: A filter function to create a subset of files we want to get cache rate for. This function accepts one argument: a [PerformanceResourceTiming](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming) object. The default is no filter (all resources). +- **limit**: A number, in milliseconds, which represents resource load duration that we consider served from cache. The default is 50. + +> † There are two types of browser cache: memory cache and disk cache. Memory cache will have a duration of 0 (limit: 0) and disk cache will have higher duration, while still relatively low. For this reason the default limit value is 50 (milliseconds). This gives a close estimation of browser cache hit-rate. + ### `fps` Measure page frame rate at a certain point in time ```js diff --git a/package.json b/package.json index c2e79e5..0490c68 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "page-timing", - "version": "3.1.1", + "version": "3.2.0", "description": "⏱ Collect and measure browser performance metrics", "keywords": [ "browser", diff --git a/src/cachehit/index.js b/src/cachehit/index.js new file mode 100644 index 0000000..da6fdd0 --- /dev/null +++ b/src/cachehit/index.js @@ -0,0 +1,28 @@ +/** + * Check window frames per second rate + * @param {function} filter + * @param {number} limit + * @returns {number?} + */ +export const cachehit = ({ filter = () => true, limit = 50 } = {}) => new Promise( + ((resolve, reject) => { + try { + const entries = window.performance.getEntriesByType('resource').filter(filter); + console.log(entries.length); + + if (!entries.length) { + resolve(undefined); + return; + } + + const cached = entries.filter( + ({ duration }) => duration < limit + ); + resolve( + cached.length / entries.length + ); + } catch (error) { + reject(error); + } + }) +); diff --git a/src/cachehit/spec.js b/src/cachehit/spec.js new file mode 100644 index 0000000..6928047 --- /dev/null +++ b/src/cachehit/spec.js @@ -0,0 +1,31 @@ +import { getEntriesByTypeMock } from '../../spec-helpers/index.js'; +import { cachehit } from './index.js'; + +const { getEntriesByType } = window.performance; + +describe('cachehit', () => { + before(() => { + window.performance.getEntriesByType = getEntriesByTypeMock; + }); + after(() => { + window.performance.getEntriesByType = getEntriesByType; + }); + it('should return all results cache hit rate', async() => { + expect(await cachehit()).to.equal(0.2161290322580645); + }); + it('should measure only a subset of the resources', async() => { + expect(await cachehit({ + filter: ({ name }) => /.woff2?[$\?]/.test(name), + })).to.equal(1); + }); + it('should have a lower result for a different limit', async() => { + expect(await cachehit({ + limit: 10 + })).to.equal(0.035483870967741936); + }); + it('should return undefined when no matching entries were found', async() => { + expect(await cachehit({ + filter: ({ name }) => name.includes('nothing.com') + })).to.be.undefined; + }); +});