Delete the old ReactPerf

This commit is contained in:
Dan Abramov
2016-04-28 20:43:49 +01:00
parent 222f5087fe
commit 5f8f1f6c16
19 changed files with 15 additions and 1095 deletions
+1 -4
View File
@@ -16,7 +16,6 @@
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDefaultInjection = require('ReactDefaultInjection');
var ReactMount = require('ReactMount');
var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var ReactUpdates = require('ReactUpdates');
var ReactVersion = require('ReactVersion');
@@ -28,11 +27,9 @@ var warning = require('warning');
ReactDefaultInjection.inject();
var render = ReactPerf.measure('React', 'render', ReactMount.render);
var React = {
findDOMNode: findDOMNode,
render: render,
render: ReactMount.render,
unmountComponentAtNode: ReactMount.unmountComponentAtNode,
version: ReactVersion,
@@ -13,7 +13,6 @@
var DOMChildrenOperations = require('DOMChildrenOperations');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactPerf = require('ReactPerf');
/**
* Operations used to process updates to DOM nodes.
@@ -32,8 +31,4 @@ var ReactDOMIDOperations = {
},
};
ReactPerf.measureMethods(ReactDOMIDOperations, 'ReactDOMIDOperations', {
dangerouslyProcessChildrenUpdates: 'dangerouslyProcessChildrenUpdates',
});
module.exports = ReactDOMIDOperations;
-6
View File
@@ -22,7 +22,6 @@ var ReactElement = require('ReactElement');
var ReactFeatureFlags = require('ReactFeatureFlags');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactMarkupChecksum = require('ReactMarkupChecksum');
var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var ReactUpdateQueue = require('ReactUpdateQueue');
var ReactUpdates = require('ReactUpdates');
@@ -711,9 +710,4 @@ var ReactMount = {
},
};
ReactPerf.measureMethods(ReactMount, 'ReactMount', {
_renderNewRootComponent: '_renderNewRootComponent',
_mountImageIntoNode: '_mountImageIntoNode',
});
module.exports = ReactMount;
@@ -16,7 +16,6 @@ var Danger = require('Danger');
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactPerf = require('ReactPerf');
var createMicrosoftUnsafeLocalFunction = require('createMicrosoftUnsafeLocalFunction');
var setInnerHTML = require('setInnerHTML');
@@ -239,8 +238,4 @@ var DOMChildrenOperations = {
};
ReactPerf.measureMethods(DOMChildrenOperations, 'DOMChildrenOperations', {
replaceDelimitedText: 'replaceDelimitedText',
});
module.exports = DOMChildrenOperations;
@@ -14,7 +14,6 @@
var CSSProperty = require('CSSProperty');
var ExecutionEnvironment = require('ExecutionEnvironment');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactPerf = require('ReactPerf');
var camelizeStyleName = require('camelizeStyleName');
var dangerousStyleValue = require('dangerousStyleValue');
@@ -238,8 +237,4 @@ var CSSPropertyOperations = {
};
ReactPerf.measureMethods(CSSPropertyOperations, 'CSSPropertyOperations', {
setValueForStyles: 'setValueForStyles',
});
module.exports = CSSPropertyOperations;
@@ -15,7 +15,6 @@ var DOMProperty = require('DOMProperty');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDOMInstrumentation = require('ReactDOMInstrumentation');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactPerf = require('ReactPerf');
var quoteAttributeValueForBrowser = require('quoteAttributeValueForBrowser');
var warning = require('warning');
@@ -250,10 +249,4 @@ var DOMPropertyOperations = {
};
ReactPerf.measureMethods(DOMPropertyOperations, 'DOMPropertyOperations', {
setValueForProperty: 'setValueForProperty',
setValueForAttribute: 'setValueForAttribute',
deleteValueForProperty: 'deleteValueForProperty',
});
module.exports = DOMPropertyOperations;
@@ -13,7 +13,6 @@
var DOMChildrenOperations = require('DOMChildrenOperations');
var ReactDOMIDOperations = require('ReactDOMIDOperations');
var ReactPerf = require('ReactPerf');
/**
* Abstracts away all functionality of the reconciler that requires knowledge of
@@ -40,12 +39,4 @@ var ReactComponentBrowserEnvironment = {
};
ReactPerf.measureMethods(
ReactComponentBrowserEnvironment,
'ReactComponentBrowserEnvironment',
{
replaceNodeWithMarkup: 'replaceNodeWithMarkup',
}
);
module.exports = ReactComponentBrowserEnvironment;
@@ -34,7 +34,6 @@ var ReactDOMSelect = require('ReactDOMSelect');
var ReactDOMTextarea = require('ReactDOMTextarea');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactMultiChild = require('ReactMultiChild');
var ReactPerf = require('ReactPerf');
var ReactServerRenderingTransaction = require('ReactServerRenderingTransaction');
var emptyFunction = require('emptyFunction');
@@ -1129,11 +1128,6 @@ ReactDOMComponent.Mixin = {
};
ReactPerf.measureMethods(ReactDOMComponent.Mixin, 'ReactDOMComponent', {
mountComponent: 'mountComponent',
receiveComponent: 'receiveComponent',
});
Object.assign(
ReactDOMComponent.prototype,
ReactDOMComponent.Mixin,
@@ -15,7 +15,6 @@ var DOMChildrenOperations = require('DOMChildrenOperations');
var DOMLazyTree = require('DOMLazyTree');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactPerf = require('ReactPerf');
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
var invariant = require('invariant');
@@ -188,13 +187,4 @@ Object.assign(ReactDOMTextComponent.prototype, {
});
ReactPerf.measureMethods(
ReactDOMTextComponent.prototype,
'ReactDOMTextComponent',
{
mountComponent: 'mountComponent',
receiveComponent: 'receiveComponent',
}
);
module.exports = ReactDOMTextComponent;
@@ -15,7 +15,6 @@ var BeforeInputEventPlugin = require('BeforeInputEventPlugin');
var ChangeEventPlugin = require('ChangeEventPlugin');
var DefaultEventPluginOrder = require('DefaultEventPluginOrder');
var EnterLeaveEventPlugin = require('EnterLeaveEventPlugin');
var ExecutionEnvironment = require('ExecutionEnvironment');
var HTMLDOMPropertyConfig = require('HTMLDOMPropertyConfig');
var ReactComponentBrowserEnvironment =
require('ReactComponentBrowserEnvironment');
@@ -91,14 +90,6 @@ function inject() {
);
ReactInjection.Component.injectEnvironment(ReactComponentBrowserEnvironment);
if (__DEV__) {
var url = (ExecutionEnvironment.canUseDOM && window.location.href) || '';
if ((/[?&]react_perf\b/).test(url)) {
var ReactDefaultPerf = require('ReactDefaultPerf');
ReactDefaultPerf.start();
}
}
}
module.exports = {
@@ -19,7 +19,6 @@ var ReactClass = require('ReactClass');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
var ReactNativeComponent = require('ReactNativeComponent');
var ReactPerf = require('ReactPerf');
var ReactUpdates = require('ReactUpdates');
var ReactInjection = {
@@ -31,7 +30,6 @@ var ReactInjection = {
EventPluginUtils: EventPluginUtils.injection,
EventEmitter: ReactBrowserEventEmitter.injection,
NativeComponent: ReactNativeComponent.injection,
Perf: ReactPerf.injection,
Updates: ReactUpdates.injection,
};
@@ -13,7 +13,6 @@
var ReactNativeComponentTree = require('ReactNativeComponentTree');
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
var ReactPerf = require('ReactPerf');
var UIManager = require('UIManager');
/**
@@ -71,12 +70,7 @@ var dangerouslyProcessChildrenUpdates = function(inst, childrenUpdates) {
* `ReactComponent.DOMIDOperations`.
*/
var ReactNativeDOMIDOperations = {
dangerouslyProcessChildrenUpdates: ReactPerf.measure(
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
'ReactDOMIDOperations',
'dangerouslyProcessChildrenUpdates',
dangerouslyProcessChildrenUpdates
),
dangerouslyProcessChildrenUpdates,
/**
* Replaces a view that exists in the document with markup.
@@ -84,14 +78,10 @@ var ReactNativeDOMIDOperations = {
* @param {string} id ID of child to be replaced.
* @param {string} markup Mount image to replace child with id.
*/
dangerouslyReplaceNodeWithMarkupByID: ReactPerf.measure(
'ReactDOMIDOperations',
'dangerouslyReplaceNodeWithMarkupByID',
function(id, mountImage) {
var oldTag = id;
UIManager.replaceExistingNonRootView(oldTag, mountImage);
}
),
dangerouslyReplaceNodeWithMarkupByID: function(id, mountImage) {
var oldTag = id;
UIManager.replaceExistingNonRootView(oldTag, mountImage);
},
};
module.exports = ReactNativeDOMIDOperations;
+9 -21
View File
@@ -15,7 +15,6 @@ var ReactElement = require('ReactElement');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactNativeContainerInfo = require('ReactNativeContainerInfo');
var ReactNativeTagHandles = require('ReactNativeTagHandles');
var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var ReactUpdateQueue = require('ReactUpdateQueue');
var ReactUpdates = require('ReactUpdates');
@@ -171,20 +170,15 @@ var ReactNativeMount = {
* @param {View} view View tree image.
* @param {number} containerViewID View to insert sub-view into.
*/
_mountImageIntoNode: ReactPerf.measure(
// FIXME(frantic): #4441289 Hack to avoid modifying react-tools
'ReactComponentBrowserEnvironment',
'mountImageIntoNode',
function(mountImage, containerID) {
// Since we now know that the `mountImage` has been mounted, we can
// mark it as such.
var childTag = mountImage;
UIManager.setChildren(
containerID,
[childTag]
);
}
),
_mountImageIntoNode: function(mountImage, containerID) {
// Since we now know that the `mountImage` has been mounted, we can
// mark it as such.
var childTag = mountImage;
UIManager.setChildren(
containerID,
[childTag]
);
},
/**
* Standard unmounting of the component that is rendered into `containerID`,
@@ -242,10 +236,4 @@ var ReactNativeMount = {
};
ReactNativeMount.renderComponent = ReactPerf.measure(
'ReactMount',
'_renderNewRootComponent',
ReactNativeMount.renderComponent
);
module.exports = ReactNativeMount;
@@ -18,7 +18,6 @@ var ReactErrorUtils = require('ReactErrorUtils');
var ReactInstanceMap = require('ReactInstanceMap');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactNodeTypes = require('ReactNodeTypes');
var ReactPerf = require('ReactPerf');
var ReactPropTypeLocations = require('ReactPropTypeLocations');
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactReconciler = require('ReactReconciler');
@@ -1210,16 +1209,6 @@ var ReactCompositeComponentMixin = {
};
ReactPerf.measureMethods(
ReactCompositeComponentMixin,
'ReactCompositeComponent',
{
mountComponent: 'mountComponent',
updateComponent: 'updateComponent',
_renderValidatedComponent: '_renderValidatedComponent',
}
);
var ReactCompositeComponent = {
Mixin: ReactCompositeComponentMixin,
@@ -15,7 +15,6 @@ var CallbackQueue = require('CallbackQueue');
var PooledClass = require('PooledClass');
var ReactFeatureFlags = require('ReactFeatureFlags');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactPerf = require('ReactPerf');
var ReactReconciler = require('ReactReconciler');
var Transaction = require('Transaction');
@@ -222,11 +221,6 @@ var flushBatchedUpdates = function() {
ReactInstrumentation.debugTool.onEndFlush();
}
};
flushBatchedUpdates = ReactPerf.measure(
'ReactUpdates',
'flushBatchedUpdates',
flushBatchedUpdates
);
/**
* Mark a component as needing a rerender, adding an optional callback to a
-361
View File
@@ -1,361 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactDefaultPerf
*/
'use strict';
var DOMProperty = require('DOMProperty');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactDefaultPerfAnalysis = require('ReactDefaultPerfAnalysis');
var ReactMount = require('ReactMount');
var ReactPerf = require('ReactPerf');
var performanceNow = require('performanceNow');
var warning = require('warning');
function roundFloat(val) {
return Math.floor(val * 100) / 100;
}
function addValue(obj, key, val) {
obj[key] = (obj[key] || 0) + val;
}
// Composite/text components don't have any built-in ID: we have to make our own
var compositeIDMap;
var compositeIDCounter = 17000;
function getIDOfComposite(inst) {
if (!compositeIDMap) {
compositeIDMap = new WeakMap();
}
if (compositeIDMap.has(inst)) {
return compositeIDMap.get(inst);
} else {
var id = compositeIDCounter++;
compositeIDMap.set(inst, id);
return id;
}
}
function getID(inst) {
if (inst.hasOwnProperty('_rootNodeID')) {
return inst._rootNodeID;
} else {
return getIDOfComposite(inst);
}
}
function stripComplexValues(key, value) {
if (typeof value !== 'object' || Array.isArray(value) || value == null) {
return value;
}
var prototype = Object.getPrototypeOf(value);
if (!prototype || prototype === Object.prototype) {
return value;
}
return '<not serializable>';
}
// This implementation of ReactPerf is going away some time mid 15.x.
// While we plan to keep most of the API, the actual format of measurements
// will change dramatically. To signal this, we wrap them into an opaque-ish
// object to discourage reaching into it until the API stabilizes.
function wrapLegacyMeasurements(measurements) {
return { __unstable_this_format_will_change: measurements };
}
function unwrapLegacyMeasurements(measurements) {
return measurements && measurements.__unstable_this_format_will_change || measurements;
}
var warnedAboutPrintDOM = false;
var warnedAboutGetMeasurementsSummaryMap = false;
var ReactDefaultPerf = {
_allMeasurements: [], // last item in the list is the current one
_mountStack: [0],
_compositeStack: [],
_injected: false,
start: function() {
if (!ReactDefaultPerf._injected) {
ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure);
}
ReactDefaultPerf._allMeasurements.length = 0;
ReactPerf.enableMeasure = true;
},
stop: function() {
ReactPerf.enableMeasure = false;
},
getLastMeasurements: function() {
return wrapLegacyMeasurements(ReactDefaultPerf._allMeasurements);
},
printExclusive: function(measurements) {
measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
var summary = ReactDefaultPerfAnalysis.getExclusiveSummary(measurements);
console.table(summary.map(function(item) {
return {
'Component class name': item.componentName,
'Total inclusive time (ms)': roundFloat(item.inclusive),
'Exclusive mount time (ms)': roundFloat(item.exclusive),
'Exclusive render time (ms)': roundFloat(item.render),
'Mount time per instance (ms)': roundFloat(item.exclusive / item.count),
'Render time per instance (ms)': roundFloat(item.render / item.count),
'Instances': item.count,
};
}));
// TODO: ReactDefaultPerfAnalysis.getTotalTime() does not return the correct
// number.
},
printInclusive: function(measurements) {
measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(measurements);
console.table(summary.map(function(item) {
return {
'Owner > component': item.componentName,
'Inclusive time (ms)': roundFloat(item.time),
'Instances': item.count,
};
}));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
);
},
getMeasurementsSummaryMap: function(measurements) {
warning(
warnedAboutGetMeasurementsSummaryMap,
'`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
'`ReactPerf.getWasted(...)` instead.'
);
warnedAboutGetMeasurementsSummaryMap = true;
return ReactDefaultPerf.getWasted(measurements);
},
getWasted: function(measurements) {
measurements = unwrapLegacyMeasurements(measurements);
var summary = ReactDefaultPerfAnalysis.getInclusiveSummary(
measurements,
true
);
return summary.map(function(item) {
return {
'Owner > component': item.componentName,
'Wasted time (ms)': item.time,
'Instances': item.count,
};
});
},
printWasted: function(measurements) {
measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
console.table(ReactDefaultPerf.getWasted(measurements));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
);
},
printDOM: function(measurements) {
warning(
warnedAboutPrintDOM,
'`ReactPerf.printDOM(...)` is deprecated. Use ' +
'`ReactPerf.printOperations(...)` instead.'
);
warnedAboutPrintDOM = true;
return ReactDefaultPerf.printOperations(measurements);
},
printOperations: function(measurements) {
measurements = unwrapLegacyMeasurements(measurements || ReactDefaultPerf._allMeasurements);
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
console.table(summary.map(function(item) {
var result = {};
result[DOMProperty.ID_ATTRIBUTE_NAME] = item.id;
result.type = item.type;
result.args = JSON.stringify(item.args, stripComplexValues);
return result;
}));
console.log(
'Total time:',
ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'
);
},
_recordWrite: function(id, fnName, totalTime, args) {
// TODO: totalTime isn't that useful since it doesn't count paints/reflows
var entry =
ReactDefaultPerf
._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1];
var writes = entry.writes;
writes[id] = writes[id] || [];
writes[id].push({
type: fnName,
time: totalTime,
args: args,
});
},
measure: function(moduleName, fnName, func) {
return function(...args) {
var totalTime;
var rv;
var start;
var entry = ReactDefaultPerf._allMeasurements[
ReactDefaultPerf._allMeasurements.length - 1
];
if (fnName === '_renderNewRootComponent' ||
fnName === 'flushBatchedUpdates') {
// A "measurement" is a set of metrics recorded for each flush. We want
// to group the metrics for a given flush together so we can look at the
// components that rendered and the DOM operations that actually
// happened to determine the amount of "wasted work" performed.
ReactDefaultPerf._allMeasurements.push(entry = {
exclusive: {},
inclusive: {},
render: {},
counts: {},
writes: {},
displayNames: {},
hierarchy: {},
totalTime: 0,
created: {},
});
start = performanceNow();
rv = func.apply(this, args);
entry.totalTime = performanceNow() - start;
return rv;
} else if (fnName === '_mountImageIntoNode' ||
moduleName === 'ReactDOMIDOperations' ||
moduleName === 'CSSPropertyOperations' ||
moduleName === 'DOMChildrenOperations' ||
moduleName === 'DOMPropertyOperations' ||
moduleName === 'ReactComponentBrowserEnvironment') {
start = performanceNow();
rv = func.apply(this, args);
totalTime = performanceNow() - start;
if (fnName === '_mountImageIntoNode') {
ReactDefaultPerf._recordWrite('', fnName, totalTime, args[0]);
} else if (fnName === 'dangerouslyProcessChildrenUpdates') {
// special format
args[1].forEach(function(update) {
var writeArgs = {};
if (update.fromIndex !== null) {
writeArgs.fromIndex = update.fromIndex;
}
if (update.toIndex !== null) {
writeArgs.toIndex = update.toIndex;
}
if (update.content !== null) {
writeArgs.content = update.content;
}
ReactDefaultPerf._recordWrite(
args[0]._rootNodeID,
update.type,
totalTime,
writeArgs
);
});
} else {
// basic format
var id = args[0];
if (moduleName === 'EventPluginHub') {
id = id._rootNodeID;
} else if (fnName === 'replaceNodeWithMarkup') {
// Old node is already unmounted; can't get its instance
id = ReactDOMComponentTree.getInstanceFromNode(args[1].node)._rootNodeID;
} else if (fnName === 'replaceDelimitedText') {
id = getID(ReactDOMComponentTree.getInstanceFromNode(args[0]));
} else if (typeof id === 'object') {
id = getID(ReactDOMComponentTree.getInstanceFromNode(args[0]));
}
ReactDefaultPerf._recordWrite(
id,
fnName,
totalTime,
Array.prototype.slice.call(args, 1)
);
}
return rv;
} else if (moduleName === 'ReactCompositeComponent' && (
fnName === 'mountComponent' ||
fnName === 'updateComponent' || // TODO: receiveComponent()?
fnName === '_renderValidatedComponent')) {
if (this._currentElement.type === ReactMount.TopLevelWrapper) {
return func.apply(this, args);
}
var rootNodeID = getIDOfComposite(this);
var isRender = fnName === '_renderValidatedComponent';
var isMount = fnName === 'mountComponent';
var mountStack = ReactDefaultPerf._mountStack;
if (isRender) {
addValue(entry.counts, rootNodeID, 1);
} else if (isMount) {
entry.created[rootNodeID] = true;
mountStack.push(0);
}
ReactDefaultPerf._compositeStack.push(rootNodeID);
start = performanceNow();
rv = func.apply(this, args);
totalTime = performanceNow() - start;
ReactDefaultPerf._compositeStack.pop();
if (isRender) {
addValue(entry.render, rootNodeID, totalTime);
} else if (isMount) {
var subMountTime = mountStack.pop();
mountStack[mountStack.length - 1] += totalTime;
addValue(entry.exclusive, rootNodeID, totalTime - subMountTime);
addValue(entry.inclusive, rootNodeID, totalTime);
} else {
addValue(entry.inclusive, rootNodeID, totalTime);
}
entry.displayNames[rootNodeID] = {
current: this.getName(),
owner: this._currentElement._owner ?
this._currentElement._owner.getName() :
'<root>',
};
return rv;
} else if (
(moduleName === 'ReactDOMComponent' ||
moduleName === 'ReactDOMTextComponent') &&
(fnName === 'mountComponent' ||
fnName === 'receiveComponent')) {
rv = func.apply(this, args);
entry.hierarchy[getID(this)] =
ReactDefaultPerf._compositeStack.slice();
return rv;
} else {
return func.apply(this, args);
}
};
},
};
module.exports = ReactDefaultPerf;
-214
View File
@@ -1,214 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactDefaultPerfAnalysis
*/
'use strict';
// Don't try to save users less than 1.2ms (a number I made up)
var DONT_CARE_THRESHOLD = 1.2;
var DOM_OPERATION_TYPES = {
'_mountImageIntoNode': 'set innerHTML',
INSERT_MARKUP: 'set innerHTML',
MOVE_EXISTING: 'move',
REMOVE_NODE: 'remove',
SET_MARKUP: 'set innerHTML',
TEXT_CONTENT: 'set textContent',
'setValueForProperty': 'update attribute',
'setValueForAttribute': 'update attribute',
'deleteValueForProperty': 'remove attribute',
'setValueForStyles': 'update styles',
'replaceNodeWithMarkup': 'replace',
'replaceDelimitedText': 'replace',
};
function getTotalTime(measurements) {
// TODO: return number of DOM ops? could be misleading.
// TODO: measure dropped frames after reconcile?
// TODO: log total time of each reconcile and the top-level component
// class that triggered it.
var totalTime = 0;
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
totalTime += measurement.totalTime;
}
return totalTime;
}
function getDOMSummary(measurements) {
var items = [];
measurements.forEach(function(measurement) {
Object.keys(measurement.writes).forEach(function(id) {
measurement.writes[id].forEach(function(write) {
items.push({
id: id,
type: DOM_OPERATION_TYPES[write.type] || write.type,
args: write.args,
});
});
});
});
return items;
}
function getExclusiveSummary(measurements) {
var candidates = {};
var displayName;
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
var allIDs = Object.assign(
{},
measurement.exclusive,
measurement.inclusive
);
for (var id in allIDs) {
displayName = measurement.displayNames[id].current;
candidates[displayName] = candidates[displayName] || {
componentName: displayName,
inclusive: 0,
exclusive: 0,
render: 0,
count: 0,
};
if (measurement.render[id]) {
candidates[displayName].render += measurement.render[id];
}
if (measurement.exclusive[id]) {
candidates[displayName].exclusive += measurement.exclusive[id];
}
if (measurement.inclusive[id]) {
candidates[displayName].inclusive += measurement.inclusive[id];
}
if (measurement.counts[id]) {
candidates[displayName].count += measurement.counts[id];
}
}
}
// Now make a sorted array with the results.
var arr = [];
for (displayName in candidates) {
if (candidates[displayName].exclusive >= DONT_CARE_THRESHOLD) {
arr.push(candidates[displayName]);
}
}
arr.sort(function(a, b) {
return b.exclusive - a.exclusive;
});
return arr;
}
function getInclusiveSummary(measurements, onlyClean) {
var candidates = {};
var inclusiveKey;
for (var i = 0; i < measurements.length; i++) {
var measurement = measurements[i];
var allIDs = Object.assign(
{},
measurement.exclusive,
measurement.inclusive
);
var cleanComponents;
if (onlyClean) {
cleanComponents = getUnchangedComponents(measurement);
}
for (var id in allIDs) {
if (onlyClean && !cleanComponents[id]) {
continue;
}
var displayName = measurement.displayNames[id];
// Inclusive time is not useful for many components without knowing where
// they are instantiated. So we aggregate inclusive time with both the
// owner and current displayName as the key.
inclusiveKey = displayName.owner + ' > ' + displayName.current;
candidates[inclusiveKey] = candidates[inclusiveKey] || {
componentName: inclusiveKey,
time: 0,
count: 0,
};
if (measurement.inclusive[id]) {
candidates[inclusiveKey].time += measurement.inclusive[id];
}
if (measurement.counts[id]) {
candidates[inclusiveKey].count += measurement.counts[id];
}
}
}
// Now make a sorted array with the results.
var arr = [];
for (inclusiveKey in candidates) {
if (candidates[inclusiveKey].time >= DONT_CARE_THRESHOLD) {
arr.push(candidates[inclusiveKey]);
}
}
arr.sort(function(a, b) {
return b.time - a.time;
});
return arr;
}
function getUnchangedComponents(measurement) {
// For a given reconcile, look at which components did not actually
// render anything to the DOM and return a mapping of their ID to
// the amount of time it took to render the entire subtree.
var cleanComponents = {};
var writes = measurement.writes;
var hierarchy = measurement.hierarchy;
var dirtyComposites = {};
Object.keys(writes).forEach(function(id) {
writes[id].forEach(function(write) {
// Root mounting (innerHTML set) is recorded with an ID of ''
if (id !== '' && hierarchy.hasOwnProperty(id)) {
hierarchy[id].forEach((c) => dirtyComposites[c] = true);
}
});
});
var allIDs = Object.assign({}, measurement.exclusive, measurement.inclusive);
for (var id in allIDs) {
var isDirty = false;
// See if any of the DOM operations applied to this component's subtree.
if (dirtyComposites[id]) {
isDirty = true;
}
// check if component newly created
if (measurement.created[id]) {
isDirty = true;
}
if (!isDirty && measurement.counts[id] > 0) {
cleanComponents[id] = true;
}
}
return cleanComponents;
}
var ReactDefaultPerfAnalysis = {
getExclusiveSummary: getExclusiveSummary,
getInclusiveSummary: getInclusiveSummary,
getDOMSummary: getDOMSummary,
getTotalTime: getTotalTime,
};
module.exports = ReactDefaultPerfAnalysis;
-100
View File
@@ -1,100 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactPerf
*/
'use strict';
/**
* ReactPerf is a general AOP system designed to measure performance. This
* module only has the hooks: see ReactDefaultPerf for the analysis tool.
* See also https://facebook.github.io/react/docs/perf.html
*/
var ReactPerf = {
/**
* Boolean to enable/disable measurement. Set to false by default to prevent
* accidental logging and perf loss.
*/
enableMeasure: false,
/**
* Holds onto the measure function in use. By default, don't measure
* anything, but we'll override this if we inject a measure function.
*/
storedMeasure: _noMeasure,
/**
* @param {object} object
* @param {string} objectName
* @param {object<string>} methodNames
*/
measureMethods: function(object, objectName, methodNames) {
if (__DEV__) {
for (var key in methodNames) {
if (!methodNames.hasOwnProperty(key)) {
continue;
}
object[key] = ReactPerf.measure(
objectName,
methodNames[key],
object[key]
);
}
}
},
/**
* Use this to wrap methods you want to measure. Zero overhead in production.
*
* @param {string} objName
* @param {string} fnName
* @param {function} func
* @return {function}
*/
measure: function(objName, fnName, func) {
if (__DEV__) {
var measuredFunc = null;
var wrapper = function() {
if (ReactPerf.enableMeasure) {
if (!measuredFunc) {
measuredFunc = ReactPerf.storedMeasure(objName, fnName, func);
}
return measuredFunc.apply(this, arguments);
}
return func.apply(this, arguments);
};
wrapper.displayName = objName + '_' + fnName;
return wrapper;
}
return func;
},
injection: {
/**
* @param {function} measure
*/
injectMeasure: function(measure) {
ReactPerf.storedMeasure = measure;
},
},
};
/**
* Simply passes through the measured function, without measuring it.
*
* @param {string} objName
* @param {string} fnName
* @param {function} func
* @return {function}
*/
function _noMeasure(objName, fnName, func) {
return func;
}
module.exports = ReactPerf;
-299
View File
@@ -1,299 +0,0 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
describe('ReactDefaultPerf', function() {
var React;
var ReactDOM;
var ReactDOMFeatureFlags;
var ReactDefaultPerf;
var ReactTestUtils;
var ReactDefaultPerfAnalysis;
var App;
var Box;
var Div;
beforeEach(function() {
var now = 0;
jest.setMock('fbjs/lib/performanceNow', function() {
return now++;
});
if (typeof console.table !== 'function') {
console.table = () => {};
console.table.isFake = true;
}
React = require('React');
ReactDOM = require('ReactDOM');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
ReactDefaultPerf = require('ReactDefaultPerf');
ReactTestUtils = require('ReactTestUtils');
ReactDefaultPerfAnalysis = require('ReactDefaultPerfAnalysis');
App = React.createClass({
render: function() {
return <div><Box /><Box flip={this.props.flipSecond} /></div>;
},
});
Box = React.createClass({
render: function() {
return <div key={!!this.props.flip}><input /></div>;
},
});
// ReactPerf only measures composites, so we put everything in one.
Div = React.createClass({
render: function() {
return <div {...this.props} />;
},
});
});
afterEach(function() {
if (console.table.isFake) {
delete console.table;
}
});
function measure(fn) {
ReactDefaultPerf.start();
fn();
ReactDefaultPerf.stop();
return ReactDefaultPerf.getLastMeasurements().__unstable_this_format_will_change;
}
it('should count no-op update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<App />, container);
var measurements = measure(() => {
ReactDOM.render(<App />, container);
});
var summary = ReactDefaultPerf.getWasted(measurements);
expect(summary.length).toBe(2);
/*eslint-disable dot-notation */
expect(summary[0]['Owner > component']).toBe('<root> > App');
expect(summary[0]['Wasted time (ms)']).not.toBe(0);
expect(summary[0]['Instances']).toBe(1);
expect(summary[1]['Owner > component']).toBe('App > Box');
expect(summary[1]['Wasted time (ms)']).not.toBe(0);
expect(summary[1]['Instances']).toBe(2);
/*eslint-enable dot-notation */
});
it('should count no-op update in child as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<App />, container);
// Here, we add a Box -- two of the <Box /> updates are wasted time (but the
// addition of the third is not)
var measurements = measure(() => {
ReactDOM.render(<App flipSecond={true} />, container);
});
var summary = ReactDefaultPerf.getWasted(measurements);
expect(summary.length).toBe(1);
/*eslint-disable dot-notation */
expect(summary[0]['Owner > component']).toBe('App > Box');
expect(summary[0]['Wasted time (ms)']).not.toBe(0);
expect(summary[0]['Instances']).toBe(1);
/*eslint-enable dot-notation */
});
function expectNoWaste(fn) {
var measurements = measure(fn);
var summary = ReactDefaultPerf.getWasted(measurements);
expect(summary).toEqual([]);
}
it('should not count initial render as waste', function() {
expectNoWaste(() => {
ReactTestUtils.renderIntoDocument(<App />);
});
});
it('should not count unmount as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div>hello</Div>, container);
expectNoWaste(() => {
ReactDOM.unmountComponentAtNode(container);
});
});
it('should not count content update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div>hello</Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div>hello world</Div>, container);
});
});
it('should not count child addition as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div><span /></Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div><span /><span /></Div>, container);
});
});
it('should not count child removal as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div><span /><span /></Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div><span /></Div>, container);
});
});
it('should not count property update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div className="yellow">hey</Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div className="blue">hey</Div>, container);
});
});
it('should not count style update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div style={{color: 'yellow'}}>hey</Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div style={{color: 'blue'}}>hey</Div>, container);
});
});
it('should not count property removal as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div className="yellow">hey</Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div>hey</Div>, container);
});
});
it('should not count raw HTML update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(
<Div dangerouslySetInnerHTML={{__html: 'me'}} />,
container
);
expectNoWaste(() => {
ReactDOM.render(
<Div dangerouslySetInnerHTML={{__html: 'you'}} />,
container
);
});
});
it('should not count child reordering as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div><div key="A" /><div key="B" /></Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div><div key="B" /><div key="A" /></Div>, container);
});
});
it('should not count text update as waste', function() {
var container = document.createElement('div');
ReactDOM.render(<Div>{'hello'}{'world'}</Div>, container);
expectNoWaste(() => {
ReactDOM.render(<Div>{'hello'}{'friend'}</Div>, container);
});
});
it('putListener should not be instrumented', function() {
var container = document.createElement('div');
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
var measurements = measure(() => {
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
});
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
expect(summary).toEqual([]);
});
it('deleteListener should not be instrumented', function() {
var container = document.createElement('div');
ReactDOM.render(<Div onClick={function() {}}>hey</Div>, container);
var measurements = measure(() => {
ReactDOM.render(<Div>hey</Div>, container);
});
var summary = ReactDefaultPerfAnalysis.getDOMSummary(measurements);
expect(summary).toEqual([]);
});
it('should not fail on input change events', function() {
var container = document.createElement('div');
var onChange = () => {};
var input = ReactDOM.render(
<input checked={true} onChange={onChange} />,
container
);
expectNoWaste(() => {
ReactTestUtils.Simulate.change(input);
});
});
it('should print a table after calling printOperations', function() {
var container = document.createElement('div');
var measurements = measure(() => {
ReactDOM.render(<Div>hey</Div>, container);
});
spyOn(console, 'table');
ReactDefaultPerf.printOperations(measurements);
expect(console.table.calls.length).toBe(1);
expect(console.table.argsForCall[0][0]).toEqual([{
'data-reactid': '',
type: 'set innerHTML',
args: ReactDOMFeatureFlags.useCreateElement ?
'{"node":"<not serializable>","children":[],"html":null,"text":null}' :
'"<div data-reactroot=\\"\\" data-reactid=\\"1\\">hey</div>"',
}]);
});
it('warns once when using getMeasurementsSummaryMap', function() {
var measurements = measure(() => {});
spyOn(console, 'error');
ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
expect(console.error.calls.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain(
'`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' +
'`ReactPerf.getWasted(...)` instead.'
);
ReactDefaultPerf.getMeasurementsSummaryMap(measurements);
expect(console.error.calls.length).toBe(1);
});
it('warns once when using printDOM', function() {
var measurements = measure(() => {});
spyOn(console, 'error');
ReactDefaultPerf.printDOM(measurements);
expect(console.error.calls.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain(
'`ReactPerf.printDOM(...)` is deprecated. Use ' +
'`ReactPerf.printOperations(...)` instead.'
);
ReactDefaultPerf.printDOM(measurements);
expect(console.error.calls.length).toBe(1);
});
});