From e0da2a910a2c249843b5796a4f04b6fca579cb9e Mon Sep 17 00:00:00 2001 From: Alexander Gusman Date: Sun, 21 Aug 2022 21:19:42 +0300 Subject: [PATCH 1/4] feat: add onReady feature --- README.md | 21 +++++++++++- lib/cli.js | 8 ++++- lib/client.js | 29 ++++++++++++---- lib/index.d.ts | 7 +++- lib/index.js | 3 +- lib/webpack.js | 5 ++- package.json | 3 ++ test/cli.js | 9 +++++ test/fixtures/benchmark-onready.tsx | 52 +++++++++++++++++++++++++++++ 9 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 test/fixtures/benchmark-onready.tsx diff --git a/README.md b/README.md index 747bcf8..3a70cc3 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Options Path to a JavaScript or TypeScript file that exports the function to be benchmarked. --debug, -d Run a development build instead of a production build to aid debugging. --devtools, -t Run Chrome in windowed mode with the devtools open. + --onReady Measure time not until the first render, but until `onReady` callback is not invoked from the component. --cpuThrottle=X Run Chrome with CPU throttled X times. --version Prints the version. --help Prints this message. @@ -77,7 +78,7 @@ Path to the benchmark file to run. See the [Usage](#usage) section for more deta #### options Type: `Object` -Default: `{ debug: false, devtools: false, cpuThrottle: 1 }` +Default: `{ debug: false, devtools: false, cpuThrottle: 1, onReady: false }` Optional object containing additional options. @@ -95,6 +96,24 @@ Default: `false` Run Chrome in windowed mode with the devtools open. +##### onReady + +Type: `boolean`
+Default: `false` + +Measure time not until the first render, but until `onReady` callback is not invoked from the component. Useful when you have something happening inside your component after the initial render. If enabled, a special `onReady` function is passed to the component as a prop, which can be called after the component has done all the initial tasks, for example: + +```tsx +// Your component + +const FooComponent = ({ onReady }) => { + useEffect(() => { + doSomeHeavyCalculations().finally(() => onReady()) + }, []) + return () +} +``` + ##### cpuThrottle Type: `number`
diff --git a/lib/cli.js b/lib/cli.js index 7b952d3..9eacd7f 100755 --- a/lib/cli.js +++ b/lib/cli.js @@ -15,6 +15,7 @@ Options Path to a JavaScript or TypeScript file that exports the function to be benchmarked. --debug, -d Run a development build instead of a production build to aid debugging. --devtools, -t Run Chrome in windowed mode with the devtools open. + --onReady Measure time not until the first render, but until onReady callback is not invoked from the component. --cpuThrottle=X Run Chrome with CPU throttled X times. --version Prints the version. --help Prints this message. @@ -33,6 +34,10 @@ Examples default: false, alias: 't', }, + onReady: { + type: 'boolean', + default: false, + }, cpuThrottle: { type: 'number', default: 1, @@ -49,7 +54,7 @@ async function main() { } const [filepath] = cli.input - const { debug, devtools, cpuThrottle } = cli.flags + const { debug, devtools, cpuThrottle, onReady } = cli.flags spinner = ora().start() @@ -87,6 +92,7 @@ async function main() { debug, devtools, cpuThrottle, + onReady, }) spinner.stop() diff --git a/lib/client.js b/lib/client.js index 1098e7f..f2487fe 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1,5 +1,6 @@ import benchmark from 'benchmark' import lodash from 'lodash' +import React from 'react' import ReactDOM from 'react-dom' import testComponent from 'react-benchmark-test-component' @@ -7,19 +8,33 @@ import testComponent from 'react-benchmark-test-component' const Benchmark = benchmark.runInContext({ _: lodash }) window.Benchmark = Benchmark -// Render an instance in the DOM before running the benchmark to make debugging easier -const container = document.createElement('div') -ReactDOM.render(testComponent(), container) -document.body.append(container) +let container = document.createElement('div') const bench = new Benchmark({ defer: true, async: true, fn(deferred) { - const container = document.createElement('div') - ReactDOM.render(testComponent(), container, () => { + container.remove() + container = document.createElement('div') + document.body.append(container) + const handleReady = () => { + if (process.env.IS_ON_READY_ENABLED !== 'true') { + return + } deferred.resolve() - }) + } + ReactDOM.render( + React.createElement(testComponent, { + onReady: process.env.IS_ON_READY_ENABLED ? handleReady : undefined, + }), + container, + () => { + if (process.env.IS_ON_READY_ENABLED === 'true') { + return + } + deferred.resolve() + } + ) }, onCycle(e) { window.benchmarkProgress(JSON.stringify(e.target)) diff --git a/lib/index.d.ts b/lib/index.d.ts index 4e6b742..28d4ae3 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -21,7 +21,12 @@ export interface RunOptions { * Run Chrome with CPU throttled X times. Useful to receive more precise results between runs. * @default 1 */ - cpuThrottle?: number + cpuThrottle?: number, + /** + * Measure time not until the first render, but until onReady callback is not invoked from the component. + * @default false + */ + onReady?: boolean, } export default class ReactBenchmark extends EventEmitter { diff --git a/lib/index.js b/lib/index.js index fca496a..4f2e2b3 100644 --- a/lib/index.js +++ b/lib/index.js @@ -36,8 +36,9 @@ module.exports = class ReactBenchmark extends EventEmitter { async run( filepath, - { debug = false, devtools = false, cpuThrottle = 1 } = {} + { debug = false, devtools = false, cpuThrottle = 1, onReady = false } = {} ) { + process.env.IS_ON_READY_ENABLED = onReady if (this.running) { throw new Error('Benchmark is already running') } diff --git a/lib/webpack.js b/lib/webpack.js index 343bf2a..60349d8 100644 --- a/lib/webpack.js +++ b/lib/webpack.js @@ -54,7 +54,10 @@ exports.compile = async (outputPath, benchmarkPath, debug) => { output: { path: outputPath, }, - plugins: [new HtmlWebpackPlugin()], + plugins: [ + new HtmlWebpackPlugin(), + new webpack.EnvironmentPlugin(['IS_ON_READY_ENABLED']), + ], performance: { hints: false, }, diff --git a/package.json b/package.json index e15e7cc..b2a7696 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,9 @@ "react": { "version": "detect" } + }, + "globals": { + "process": true } }, "prettier": { diff --git a/test/cli.js b/test/cli.js index e46a238..4ac6ad0 100644 --- a/test/cli.js +++ b/test/cli.js @@ -28,3 +28,12 @@ test('throttles CPU', async (t) => { 'The difference between throttled and not throttled execution is less then normal' ) }) + +test('uses onReady cb when --onReady flag is passed', async (t) => { + const binPath = path.resolve(__dirname, '../lib/cli.js') + const fixturePath = path.resolve(__dirname, 'fixtures/benchmark-onready.tsx') + + const result = await execa(binPath, [fixturePath]) + + t.regex(result.stdout, /[0-9,]+ ops\/sec ±[0-9.]+% \(\d+ runs sampled\)/) +}) diff --git a/test/fixtures/benchmark-onready.tsx b/test/fixtures/benchmark-onready.tsx new file mode 100644 index 0000000..6628c60 --- /dev/null +++ b/test/fixtures/benchmark-onready.tsx @@ -0,0 +1,52 @@ +import React, { useState, useEffect } from 'react' + +type BenchmarkProps = { + onReady: () => void +} + +const BenchmarkOnReady: React.FC = ({ onReady }) => { + if (!onReady) { + throw new Error('onReady has not been passed from react-benchmark') + } + const NODES_COUNT = 10 + const [nodes, setNodes] = useState([]) + const getNodes = (numberOfNodes: number, page: number) => { + const newNodes: React.ReactNode[] = [] + for (let i = numberOfNodes * (page + 1); i > numberOfNodes * page; i--) { + newNodes.push( +

+ {i} bottles of beer on the wall +
+ {i} bottles of beer! +
+ Take one down, pass it around +
+ {i - 1} bottles of beer on the wall! +

+ ) + } + return newNodes + } + const addDefferedNodes = ( + nodesCount: number, + page: number, + timeout: number + ) => { + setTimeout(() => { + setNodes((n) => [...n, ...getNodes(nodesCount, page)]) + }, timeout) + } + + useEffect(() => { + addDefferedNodes(NODES_COUNT, 1, 900) + addDefferedNodes(NODES_COUNT, 0, 1000) + }) + useEffect(() => { + if (nodes.length === NODES_COUNT * 2) { + onReady() + } + }, [nodes, onReady]) + return
{nodes}
+} + +export default BenchmarkOnReady From c93abee61a9070952ef61c05230574647d7de95e Mon Sep 17 00:00:00 2001 From: Alexander Gusman Date: Wed, 24 Aug 2022 19:12:28 +0300 Subject: [PATCH 2/4] test(cli): improve onReady test speed --- test/fixtures/benchmark-onready.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/fixtures/benchmark-onready.tsx b/test/fixtures/benchmark-onready.tsx index 6628c60..b8e4d45 100644 --- a/test/fixtures/benchmark-onready.tsx +++ b/test/fixtures/benchmark-onready.tsx @@ -38,11 +38,10 @@ const BenchmarkOnReady: React.FC = ({ onReady }) => { } useEffect(() => { - addDefferedNodes(NODES_COUNT, 1, 900) - addDefferedNodes(NODES_COUNT, 0, 1000) + addDefferedNodes(NODES_COUNT, 1, 50) }) useEffect(() => { - if (nodes.length === NODES_COUNT * 2) { + if (nodes.length === NODES_COUNT) { onReady() } }, [nodes, onReady]) From 891c591292d22e3eee372d6c2733ab762bf14fef Mon Sep 17 00:00:00 2001 From: Alexander Gusman Date: Wed, 24 Aug 2022 19:17:34 +0300 Subject: [PATCH 3/4] test(cli): improve speed of onReady test --- test/fixtures/benchmark-onready.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/fixtures/benchmark-onready.tsx b/test/fixtures/benchmark-onready.tsx index b8e4d45..6a75a25 100644 --- a/test/fixtures/benchmark-onready.tsx +++ b/test/fixtures/benchmark-onready.tsx @@ -8,7 +8,7 @@ const BenchmarkOnReady: React.FC = ({ onReady }) => { if (!onReady) { throw new Error('onReady has not been passed from react-benchmark') } - const NODES_COUNT = 10 + const NODES_COUNT = 2 const [nodes, setNodes] = useState([]) const getNodes = (numberOfNodes: number, page: number) => { const newNodes: React.ReactNode[] = [] @@ -38,7 +38,7 @@ const BenchmarkOnReady: React.FC = ({ onReady }) => { } useEffect(() => { - addDefferedNodes(NODES_COUNT, 1, 50) + addDefferedNodes(NODES_COUNT, 1, 10) }) useEffect(() => { if (nodes.length === NODES_COUNT) { From 104f55215f8e22fd82cd89ea0ec588c5bced26cf Mon Sep 17 00:00:00 2001 From: Alexander Gusman Date: Wed, 24 Aug 2022 19:29:47 +0300 Subject: [PATCH 4/4] test(cli): speeding up the test --- test/fixtures/benchmark-onready.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/benchmark-onready.tsx b/test/fixtures/benchmark-onready.tsx index 6a75a25..14c2597 100644 --- a/test/fixtures/benchmark-onready.tsx +++ b/test/fixtures/benchmark-onready.tsx @@ -38,7 +38,7 @@ const BenchmarkOnReady: React.FC = ({ onReady }) => { } useEffect(() => { - addDefferedNodes(NODES_COUNT, 1, 10) + addDefferedNodes(NODES_COUNT, 1, 1) }) useEffect(() => { if (nodes.length === NODES_COUNT) {