mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
## Summary This PR adds a "perf regression tests" page to react-devtools-shell. This page is meant to be used as a performance sanity check we will run whenever we release a new version or finish a major refactor. Similar to other pages in the shell, this page can load the inline version of devtools and a test react app on the same page. But this page does not load devtools automatically like other pages. Instead, it provides a button that allows us to load devtools on-demand, so that we can easily compare perf numbers without devtools against the numbers with devtools. <img width="561" alt="image" src="https://user-images.githubusercontent.com/1001890/184059633-e4f0852c-8464-4d94-8064-1684eee626f4.png"> As a first step, this page currently only contain one test: mount/unmount a large subtree. This is to catch perf issues that devtools can cause on the react applications it's running on, which was once a bug fixed in #24863. In the future, we plan to add: - more test apps covering different scenarios - perf numbers within devtools (e.g. initial load) ## How did you test this change? In order to show this test app can actually catch the perf regression it's aiming at, I reverted #24863 locally. Here is the result: https://user-images.githubusercontent.com/1001890/184059214-9c9b308c-173b-4dd7-b815-46fbd7067073.mov As shown in the video, the time it takes to unmount the large subtree significantly increased after DevTools is loaded. For comparison, here is how it looks like before the fix was reverted: <img width="452" alt="image" src="https://user-images.githubusercontent.com/1001890/184059743-0968bc7d-4ce4-42cd-b04a-f6cbc078d4f4.png"> ## about the `requestAnimationFrame` method For this test, I used `requestAnimationFrame` to catch the time when render and commit are done. It aligns very well with the numbers reported by Chrome DevTools performance profiling. For example, in one run, the numbers reported by my method are <img width="464" alt="image" src="https://user-images.githubusercontent.com/1001890/184060228-990a4c75-f594-411a-9f85-fa5532ec8c37.png"> They are very close to the numbers reported by Chrome profiling: <img width="456" alt="image" src="https://user-images.githubusercontent.com/1001890/184060355-a15d1ec5-c296-4016-9c83-03e761f387e3.png"> <img width="354" alt="image" src="https://user-images.githubusercontent.com/1001890/184060375-19029010-3aed-4a23-890e-397cdba86d9e.png"> `<Profiler>` is not able to catch this issue here. If you are aware of a better way to do this, please kindly share with me.
204 lines
6.1 KiB
JavaScript
204 lines
6.1 KiB
JavaScript
const {resolve} = require('path');
|
|
const {DefinePlugin} = require('webpack');
|
|
const fs = require('fs');
|
|
const {
|
|
DARK_MODE_DIMMED_WARNING_COLOR,
|
|
DARK_MODE_DIMMED_ERROR_COLOR,
|
|
DARK_MODE_DIMMED_LOG_COLOR,
|
|
LIGHT_MODE_DIMMED_WARNING_COLOR,
|
|
LIGHT_MODE_DIMMED_ERROR_COLOR,
|
|
LIGHT_MODE_DIMMED_LOG_COLOR,
|
|
GITHUB_URL,
|
|
getVersionString,
|
|
} = require('react-devtools-extensions/utils');
|
|
const {resolveFeatureFlags} = require('react-devtools-shared/buildUtils');
|
|
const semver = require('semver');
|
|
|
|
const ReactVersionSrc = fs.readFileSync(require.resolve('shared/ReactVersion'));
|
|
const currentReactVersion = /export default '([^']+)';/.exec(
|
|
ReactVersionSrc,
|
|
)[1];
|
|
|
|
const NODE_ENV = process.env.NODE_ENV;
|
|
if (!NODE_ENV) {
|
|
console.error('NODE_ENV not set');
|
|
process.exit(1);
|
|
}
|
|
|
|
const TARGET = process.env.TARGET;
|
|
if (!TARGET) {
|
|
console.error('TARGET not set');
|
|
process.exit(1);
|
|
}
|
|
|
|
const EDITOR_URL = process.env.EDITOR_URL || null;
|
|
|
|
const builtModulesDir = resolve(
|
|
__dirname,
|
|
'..',
|
|
'..',
|
|
'build',
|
|
'oss-experimental',
|
|
);
|
|
|
|
const __DEV__ = NODE_ENV === 'development';
|
|
|
|
const DEVTOOLS_VERSION = getVersionString();
|
|
|
|
// If the React version isn't set, we will use the
|
|
// current React version instead. Likewise if the
|
|
// React version isnt' set, we'll use the build folder
|
|
// for both React DevTools and React
|
|
const REACT_VERSION = process.env.REACT_VERSION
|
|
? semver.coerce(process.env.REACT_VERSION).version
|
|
: currentReactVersion;
|
|
|
|
const E2E_APP_BUILD_DIR = process.env.REACT_VERSION
|
|
? resolve(__dirname, '..', '..', 'build-regression', 'node_modules')
|
|
: builtModulesDir;
|
|
|
|
const makeConfig = (entry, alias) => {
|
|
const config = {
|
|
mode: __DEV__ ? 'development' : 'production',
|
|
devtool: __DEV__ ? 'cheap-source-map' : 'source-map',
|
|
entry,
|
|
node: {
|
|
// source-maps package has a dependency on 'fs'
|
|
// but this build won't trigger that code path
|
|
fs: 'empty',
|
|
},
|
|
resolve: {
|
|
alias,
|
|
},
|
|
optimization: {
|
|
minimize: false,
|
|
},
|
|
plugins: [
|
|
new DefinePlugin({
|
|
__DEV__,
|
|
__EXPERIMENTAL__: true,
|
|
__EXTENSION__: false,
|
|
__PROFILE__: false,
|
|
__TEST__: NODE_ENV === 'test',
|
|
'process.env.GITHUB_URL': `"${GITHUB_URL}"`,
|
|
'process.env.EDITOR_URL': EDITOR_URL != null ? `"${EDITOR_URL}"` : null,
|
|
'process.env.DEVTOOLS_PACKAGE': `"react-devtools-shell"`,
|
|
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
|
|
'process.env.DARK_MODE_DIMMED_WARNING_COLOR': `"${DARK_MODE_DIMMED_WARNING_COLOR}"`,
|
|
'process.env.DARK_MODE_DIMMED_ERROR_COLOR': `"${DARK_MODE_DIMMED_ERROR_COLOR}"`,
|
|
'process.env.DARK_MODE_DIMMED_LOG_COLOR': `"${DARK_MODE_DIMMED_LOG_COLOR}"`,
|
|
'process.env.LIGHT_MODE_DIMMED_WARNING_COLOR': `"${LIGHT_MODE_DIMMED_WARNING_COLOR}"`,
|
|
'process.env.LIGHT_MODE_DIMMED_ERROR_COLOR': `"${LIGHT_MODE_DIMMED_ERROR_COLOR}"`,
|
|
'process.env.LIGHT_MODE_DIMMED_LOG_COLOR': `"${LIGHT_MODE_DIMMED_LOG_COLOR}"`,
|
|
'process.env.E2E_APP_REACT_VERSION': `"${REACT_VERSION}"`,
|
|
}),
|
|
],
|
|
module: {
|
|
rules: [
|
|
{
|
|
test: /\.js$/,
|
|
loader: 'babel-loader',
|
|
options: {
|
|
configFile: resolve(
|
|
__dirname,
|
|
'..',
|
|
'react-devtools-shared',
|
|
'babel.config.js',
|
|
),
|
|
},
|
|
},
|
|
{
|
|
test: /\.css$/,
|
|
use: [
|
|
{
|
|
loader: 'style-loader',
|
|
},
|
|
{
|
|
loader: 'css-loader',
|
|
options: {
|
|
sourceMap: true,
|
|
modules: true,
|
|
localIdentName: '[local]',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
if (TARGET === 'local') {
|
|
// Local dev server build.
|
|
config.devServer = {
|
|
hot: true,
|
|
port: 8080,
|
|
clientLogLevel: 'warning',
|
|
publicPath: '/dist/',
|
|
stats: 'errors-only',
|
|
};
|
|
} else {
|
|
// Static build to deploy somewhere else.
|
|
config.output = {
|
|
path: resolve(__dirname, 'dist'),
|
|
filename: '[name].js',
|
|
};
|
|
}
|
|
return config;
|
|
};
|
|
|
|
const app = makeConfig(
|
|
{
|
|
'app-index': './src/app/index.js',
|
|
'app-devtools': './src/app/devtools.js',
|
|
'e2e-app': './src/e2e/app.js',
|
|
'e2e-devtools': './src/e2e/devtools.js',
|
|
'e2e-devtools-regression': './src/e2e-regression/devtools.js',
|
|
'multi-left': './src/multi/left.js',
|
|
'multi-devtools': './src/multi/devtools.js',
|
|
'multi-right': './src/multi/right.js',
|
|
'e2e-regression': './src/e2e-regression/app.js',
|
|
'perf-regression-app': './src/perf-regression/app.js',
|
|
'perf-regression-devtools': './src/perf-regression/devtools.js',
|
|
},
|
|
{
|
|
react: resolve(builtModulesDir, 'react'),
|
|
'react-debug-tools': resolve(builtModulesDir, 'react-debug-tools'),
|
|
'react-devtools-feature-flags': resolveFeatureFlags('shell'),
|
|
'react-dom/client': resolve(builtModulesDir, 'react-dom/client'),
|
|
'react-dom': resolve(builtModulesDir, 'react-dom/unstable_testing'),
|
|
'react-is': resolve(builtModulesDir, 'react-is'),
|
|
scheduler: resolve(builtModulesDir, 'scheduler'),
|
|
},
|
|
);
|
|
|
|
// Prior to React 18, we use ReactDOM.render rather than
|
|
// createRoot.
|
|
// We also use a separate build folder to build the React App
|
|
// so that we can test the current DevTools against older version of React
|
|
const e2eRegressionApp = semver.lt(REACT_VERSION, '18.0.0')
|
|
? makeConfig(
|
|
{
|
|
'e2e-app-regression': './src/e2e-regression/app-legacy.js',
|
|
},
|
|
{
|
|
react: resolve(E2E_APP_BUILD_DIR, 'react'),
|
|
'react-dom': resolve(E2E_APP_BUILD_DIR, 'react-dom'),
|
|
...(semver.satisfies(REACT_VERSION, '16.5')
|
|
? {schedule: resolve(E2E_APP_BUILD_DIR, 'schedule')}
|
|
: {scheduler: resolve(E2E_APP_BUILD_DIR, 'scheduler')}),
|
|
},
|
|
)
|
|
: makeConfig(
|
|
{
|
|
'e2e-app-regression': './src/e2e-regression/app.js',
|
|
},
|
|
{
|
|
react: resolve(E2E_APP_BUILD_DIR, 'react'),
|
|
'react-dom': resolve(E2E_APP_BUILD_DIR, 'react-dom'),
|
|
'react-dom/client': resolve(E2E_APP_BUILD_DIR, 'react-dom/client'),
|
|
scheduler: resolve(E2E_APP_BUILD_DIR, 'scheduler'),
|
|
},
|
|
);
|
|
|
|
module.exports = [app, e2eRegressionApp];
|