mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 00:20:04 +01:00
[DevTools] Recommend React Performance tracks if supported when Timeline profiler is not supported (#34684)
This commit is contained in:
parent
a757cb7667
commit
bc828bf6e3
|
|
@ -78,6 +78,7 @@ import {
|
|||
__DEBUG__,
|
||||
PROFILING_FLAG_BASIC_SUPPORT,
|
||||
PROFILING_FLAG_TIMELINE_SUPPORT,
|
||||
PROFILING_FLAG_PERFORMANCE_TRACKS_SUPPORT,
|
||||
TREE_OPERATION_ADD,
|
||||
TREE_OPERATION_REMOVE,
|
||||
TREE_OPERATION_REORDER_CHILDREN,
|
||||
|
|
@ -1074,6 +1075,7 @@ export function attach(
|
|||
const supportsTogglingSuspense =
|
||||
typeof setSuspenseHandler === 'function' &&
|
||||
typeof scheduleUpdate === 'function';
|
||||
const supportsPerformanceTracks = gte(version, '19.2.0');
|
||||
|
||||
if (typeof scheduleRefresh === 'function') {
|
||||
// When Fast Refresh updates a component, the frontend may need to purge cached information.
|
||||
|
|
@ -2401,6 +2403,9 @@ export function attach(
|
|||
if (typeof injectProfilingHooks === 'function') {
|
||||
profilingFlags |= PROFILING_FLAG_TIMELINE_SUPPORT;
|
||||
}
|
||||
if (supportsPerformanceTracks) {
|
||||
profilingFlags |= PROFILING_FLAG_PERFORMANCE_TRACKS_SUPPORT;
|
||||
}
|
||||
}
|
||||
|
||||
// Set supportsStrictMode to false for production renderer builds
|
||||
|
|
|
|||
|
|
@ -30,8 +30,9 @@ export const SUSPENSE_TREE_OPERATION_REORDER_CHILDREN = 10;
|
|||
export const SUSPENSE_TREE_OPERATION_RESIZE = 11;
|
||||
export const SUSPENSE_TREE_OPERATION_SUSPENDERS = 12;
|
||||
|
||||
export const PROFILING_FLAG_BASIC_SUPPORT = 0b01;
|
||||
export const PROFILING_FLAG_TIMELINE_SUPPORT = 0b10;
|
||||
export const PROFILING_FLAG_BASIC_SUPPORT /*. */ = 0b001;
|
||||
export const PROFILING_FLAG_TIMELINE_SUPPORT /* */ = 0b010;
|
||||
export const PROFILING_FLAG_PERFORMANCE_TRACKS_SUPPORT /* */ = 0b100;
|
||||
|
||||
export const UNKNOWN_SUSPENDERS_NONE: UnknownSuspendersReason = 0; // If we had at least one debugInfo, then that might have been the reason.
|
||||
export const UNKNOWN_SUSPENDERS_REASON_PRODUCTION: UnknownSuspendersReason = 1; // We're running in prod. That might be why we had unknown suspenders.
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {inspect} from 'util';
|
|||
import {
|
||||
PROFILING_FLAG_BASIC_SUPPORT,
|
||||
PROFILING_FLAG_TIMELINE_SUPPORT,
|
||||
PROFILING_FLAG_PERFORMANCE_TRACKS_SUPPORT,
|
||||
TREE_OPERATION_ADD,
|
||||
TREE_OPERATION_REMOVE,
|
||||
TREE_OPERATION_REMOVE_ROOT,
|
||||
|
|
@ -86,12 +87,17 @@ export type Config = {
|
|||
supportsTraceUpdates?: boolean,
|
||||
};
|
||||
|
||||
const ADVANCED_PROFILING_NONE = 0;
|
||||
const ADVANCED_PROFILING_TIMELINE = 1;
|
||||
const ADVANCED_PROFILING_PERFORMANCE_TRACKS = 2;
|
||||
type AdvancedProfiling = 0 | 1 | 2;
|
||||
|
||||
export type Capabilities = {
|
||||
supportsBasicProfiling: boolean,
|
||||
hasOwnerMetadata: boolean,
|
||||
supportsStrictMode: boolean,
|
||||
supportsTogglingSuspense: boolean,
|
||||
supportsTimeline: boolean,
|
||||
supportsAdvancedProfiling: AdvancedProfiling,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -112,6 +118,7 @@ export default class Store extends EventEmitter<{
|
|||
roots: [],
|
||||
rootSupportsBasicProfiling: [],
|
||||
rootSupportsTimelineProfiling: [],
|
||||
rootSupportsPerformanceTracks: [],
|
||||
suspenseTreeMutated: [[Map<SuspenseNode['id'], SuspenseNode['id']>]],
|
||||
supportsNativeStyleEditor: [],
|
||||
supportsReloadAndProfile: [],
|
||||
|
|
@ -195,6 +202,7 @@ export default class Store extends EventEmitter<{
|
|||
// These options default to false but may be updated as roots are added and removed.
|
||||
_rootSupportsBasicProfiling: boolean = false;
|
||||
_rootSupportsTimelineProfiling: boolean = false;
|
||||
_rootSupportsPerformanceTracks: boolean = false;
|
||||
|
||||
_bridgeProtocol: BridgeProtocol | null = null;
|
||||
_unsupportedBridgeProtocolDetected: boolean = false;
|
||||
|
|
@ -474,6 +482,11 @@ export default class Store extends EventEmitter<{
|
|||
return this._rootSupportsTimelineProfiling;
|
||||
}
|
||||
|
||||
// At least one of the currently mounted roots support performance tracks.
|
||||
get rootSupportsPerformanceTracks(): boolean {
|
||||
return this._rootSupportsPerformanceTracks;
|
||||
}
|
||||
|
||||
get supportsInspectMatchingDOMElement(): boolean {
|
||||
return this._supportsInspectMatchingDOMElement;
|
||||
}
|
||||
|
|
@ -1161,11 +1174,20 @@ export default class Store extends EventEmitter<{
|
|||
const isStrictModeCompliant = operations[i] > 0;
|
||||
i++;
|
||||
|
||||
const profilerFlags = operations[i++];
|
||||
const supportsBasicProfiling =
|
||||
(operations[i] & PROFILING_FLAG_BASIC_SUPPORT) !== 0;
|
||||
(profilerFlags & PROFILING_FLAG_BASIC_SUPPORT) !== 0;
|
||||
const supportsTimeline =
|
||||
(operations[i] & PROFILING_FLAG_TIMELINE_SUPPORT) !== 0;
|
||||
i++;
|
||||
(profilerFlags & PROFILING_FLAG_TIMELINE_SUPPORT) !== 0;
|
||||
const supportsPerformanceTracks =
|
||||
(profilerFlags & PROFILING_FLAG_PERFORMANCE_TRACKS_SUPPORT) !== 0;
|
||||
let supportsAdvancedProfiling: AdvancedProfiling =
|
||||
ADVANCED_PROFILING_NONE;
|
||||
if (supportsPerformanceTracks) {
|
||||
supportsAdvancedProfiling = ADVANCED_PROFILING_PERFORMANCE_TRACKS;
|
||||
} else if (supportsTimeline) {
|
||||
supportsAdvancedProfiling = ADVANCED_PROFILING_TIMELINE;
|
||||
}
|
||||
|
||||
let supportsStrictMode = false;
|
||||
let hasOwnerMetadata = false;
|
||||
|
|
@ -1194,7 +1216,7 @@ export default class Store extends EventEmitter<{
|
|||
hasOwnerMetadata,
|
||||
supportsStrictMode,
|
||||
supportsTogglingSuspense,
|
||||
supportsTimeline,
|
||||
supportsAdvancedProfiling,
|
||||
});
|
||||
|
||||
// Not all roots support StrictMode;
|
||||
|
|
@ -1842,21 +1864,33 @@ export default class Store extends EventEmitter<{
|
|||
const prevRootSupportsProfiling = this._rootSupportsBasicProfiling;
|
||||
const prevRootSupportsTimelineProfiling =
|
||||
this._rootSupportsTimelineProfiling;
|
||||
const prevRootSupportsPerformanceTracks =
|
||||
this._rootSupportsPerformanceTracks;
|
||||
|
||||
this._hasOwnerMetadata = false;
|
||||
this._rootSupportsBasicProfiling = false;
|
||||
this._rootSupportsTimelineProfiling = false;
|
||||
this._rootSupportsPerformanceTracks = false;
|
||||
this._rootIDToCapabilities.forEach(
|
||||
({supportsBasicProfiling, hasOwnerMetadata, supportsTimeline}) => {
|
||||
({
|
||||
supportsBasicProfiling,
|
||||
hasOwnerMetadata,
|
||||
supportsAdvancedProfiling,
|
||||
}) => {
|
||||
if (supportsBasicProfiling) {
|
||||
this._rootSupportsBasicProfiling = true;
|
||||
}
|
||||
if (hasOwnerMetadata) {
|
||||
this._hasOwnerMetadata = true;
|
||||
}
|
||||
if (supportsTimeline) {
|
||||
if (supportsAdvancedProfiling === ADVANCED_PROFILING_TIMELINE) {
|
||||
this._rootSupportsTimelineProfiling = true;
|
||||
}
|
||||
if (
|
||||
supportsAdvancedProfiling === ADVANCED_PROFILING_PERFORMANCE_TRACKS
|
||||
) {
|
||||
this._rootSupportsPerformanceTracks = true;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -1872,6 +1906,12 @@ export default class Store extends EventEmitter<{
|
|||
) {
|
||||
this.emit('rootSupportsTimelineProfiling');
|
||||
}
|
||||
if (
|
||||
this._rootSupportsPerformanceTracks !==
|
||||
prevRootSupportsPerformanceTracks
|
||||
) {
|
||||
this.emit('rootSupportsPerformanceTracks');
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSuspenseTreeChanged) {
|
||||
|
|
|
|||
16
packages/react-devtools-timeline/src/Timeline.js
vendored
16
packages/react-devtools-timeline/src/Timeline.js
vendored
|
|
@ -33,8 +33,14 @@ import {TimelineSearchContextController} from './TimelineSearchContext';
|
|||
import styles from './Timeline.css';
|
||||
|
||||
export function Timeline(_: {}): React.Node {
|
||||
const {file, inMemoryTimelineData, isTimelineSupported, setFile, viewState} =
|
||||
useContext(TimelineContext);
|
||||
const {
|
||||
file,
|
||||
inMemoryTimelineData,
|
||||
isPerformanceTracksSupported,
|
||||
isTimelineSupported,
|
||||
setFile,
|
||||
viewState,
|
||||
} = useContext(TimelineContext);
|
||||
const {didRecordCommits, isProfiling} = useContext(ProfilerContext);
|
||||
|
||||
const ref = useRef(null);
|
||||
|
|
@ -95,7 +101,11 @@ export function Timeline(_: {}): React.Node {
|
|||
} else if (isTimelineSupported) {
|
||||
content = <NoProfilingData />;
|
||||
} else {
|
||||
content = <TimelineNotSupported />;
|
||||
content = (
|
||||
<TimelineNotSupported
|
||||
isPerformanceTracksSupported={isPerformanceTracksSupported}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import type {
|
|||
export type Context = {
|
||||
file: File | null,
|
||||
inMemoryTimelineData: Array<TimelineData> | null,
|
||||
isPerformanceTracksSupported: boolean,
|
||||
isTimelineSupported: boolean,
|
||||
searchInputContainerRef: RefObject,
|
||||
setFile: (file: File | null) => void,
|
||||
|
|
@ -66,6 +67,18 @@ function TimelineContextController({children}: Props): React.Node {
|
|||
},
|
||||
);
|
||||
|
||||
const isPerformanceTracksSupported = useSyncExternalStore<boolean>(
|
||||
function subscribe(callback) {
|
||||
store.addListener('rootSupportsPerformanceTracks', callback);
|
||||
return function unsubscribe() {
|
||||
store.removeListener('rootSupportsPerformanceTracks', callback);
|
||||
};
|
||||
},
|
||||
function getState() {
|
||||
return store.rootSupportsPerformanceTracks;
|
||||
},
|
||||
);
|
||||
|
||||
const inMemoryTimelineData = useSyncExternalStore<Array<TimelineData> | null>(
|
||||
function subscribe(callback) {
|
||||
store.profilerStore.addListener('isProcessingData', callback);
|
||||
|
|
@ -135,6 +148,7 @@ function TimelineContextController({children}: Props): React.Node {
|
|||
() => ({
|
||||
file,
|
||||
inMemoryTimelineData,
|
||||
isPerformanceTracksSupported,
|
||||
isTimelineSupported,
|
||||
searchInputContainerRef,
|
||||
setFile,
|
||||
|
|
@ -145,6 +159,7 @@ function TimelineContextController({children}: Props): React.Node {
|
|||
[
|
||||
file,
|
||||
inMemoryTimelineData,
|
||||
isPerformanceTracksSupported,
|
||||
isTimelineSupported,
|
||||
setFile,
|
||||
viewState,
|
||||
|
|
|
|||
|
|
@ -12,16 +12,48 @@ import {isInternalFacebookBuild} from 'react-devtools-feature-flags';
|
|||
|
||||
import styles from './TimelineNotSupported.css';
|
||||
|
||||
export default function TimelineNotSupported(): React.Node {
|
||||
type Props = {
|
||||
isPerformanceTracksSupported: boolean,
|
||||
};
|
||||
|
||||
function PerformanceTracksSupported() {
|
||||
return (
|
||||
<div className={styles.Column}>
|
||||
<div className={styles.Header}>Timeline profiling not supported.</div>
|
||||
<>
|
||||
<p className={styles.Paragraph}>
|
||||
<span>
|
||||
Timeline profiler requires a development or profiling build of{' '}
|
||||
<code className={styles.Code}>react-dom@^18</code>.
|
||||
Please use{' '}
|
||||
<a
|
||||
className={styles.Link}
|
||||
href="https://react.dev/reference/dev-tools/react-performance-tracks"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank">
|
||||
React Performance tracks
|
||||
</a>{' '}
|
||||
instead of the Timeline profiler.
|
||||
</span>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function UnknownUnsupportedReason() {
|
||||
return (
|
||||
<>
|
||||
<p className={styles.Paragraph}>
|
||||
Timeline profiler requires a development or profiling build of{' '}
|
||||
<code className={styles.Code}>react-dom@{'>='}18</code>.
|
||||
</p>
|
||||
<p className={styles.Paragraph}>
|
||||
In React 19.2 and above{' '}
|
||||
<a
|
||||
className={styles.Link}
|
||||
href="https://react.dev/reference/dev-tools/react-performance-tracks"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank">
|
||||
React Performance tracks
|
||||
</a>{' '}
|
||||
can be used instead.
|
||||
</p>
|
||||
<div className={styles.LearnMoreRow}>
|
||||
Click{' '}
|
||||
<a
|
||||
|
|
@ -33,6 +65,22 @@ export default function TimelineNotSupported(): React.Node {
|
|||
</a>{' '}
|
||||
to learn more about profiling.
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TimelineNotSupported({
|
||||
isPerformanceTracksSupported,
|
||||
}: Props): React.Node {
|
||||
return (
|
||||
<div className={styles.Column}>
|
||||
<div className={styles.Header}>Timeline profiling not supported.</div>
|
||||
|
||||
{isPerformanceTracksSupported ? (
|
||||
<PerformanceTracksSupported />
|
||||
) : (
|
||||
<UnknownUnsupportedReason />
|
||||
)}
|
||||
|
||||
{isInternalFacebookBuild && (
|
||||
<div className={styles.MetaGKRow}>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user