diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js index d363c242de984..73b06e3c05720 100644 --- a/src/renderers/dom/shared/ReactDOMComponent.js +++ b/src/renderers/dom/shared/ReactDOMComponent.js @@ -1180,9 +1180,9 @@ ReactDOMComponent.Mixin = { }; -ReactPerf.measureMethods(ReactDOMComponent, 'ReactDOMComponent', { +ReactPerf.measureMethods(ReactDOMComponent.Mixin, 'ReactDOMComponent', { mountComponent: 'mountComponent', - updateComponent: 'updateComponent', + receiveComponent: 'receiveComponent', }); assign( diff --git a/src/renderers/dom/shared/ReactDOMTextComponent.js b/src/renderers/dom/shared/ReactDOMTextComponent.js index ff94cbd8e8506..7404b61f37556 100644 --- a/src/renderers/dom/shared/ReactDOMTextComponent.js +++ b/src/renderers/dom/shared/ReactDOMTextComponent.js @@ -18,6 +18,7 @@ var DOMPropertyOperations = require('DOMPropertyOperations'); var ReactComponentBrowserEnvironment = require('ReactComponentBrowserEnvironment'); var ReactDOMComponentTree = require('ReactDOMComponentTree'); +var ReactPerf = require('ReactPerf'); var assign = require('Object.assign'); var escapeTextContentForBrowser = require('escapeTextContentForBrowser'); @@ -153,4 +154,13 @@ assign(ReactDOMTextComponent.prototype, { }); +ReactPerf.measureMethods( + ReactDOMTextComponent.prototype, + 'ReactDOMTextComponent', + { + mountComponent: 'mountComponent', + receiveComponent: 'receiveComponent', + } +); + module.exports = ReactDOMTextComponent; diff --git a/src/test/ReactDefaultPerf.js b/src/test/ReactDefaultPerf.js index f7de7934f266b..04a2fb60b1945 100644 --- a/src/test/ReactDefaultPerf.js +++ b/src/test/ReactDefaultPerf.js @@ -28,9 +28,26 @@ function addValue(obj, key, val) { obj[key] = (obj[key] || 0) + val; } +// Composites 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; + } +} + var ReactDefaultPerf = { _allMeasurements: [], // last item in the list is the current one _mountStack: [0], + _compositeStack: [], _injected: false, start: function() { @@ -125,10 +142,10 @@ var ReactDefaultPerf = { _recordWrite: function(id, fnName, totalTime, args) { // TODO: totalTime isn't that useful since it doesn't count paints/reflows - var writes = + var entry = ReactDefaultPerf - ._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1] - .writes; + ._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1]; + var writes = entry.writes; writes[id] = writes[id] || []; writes[id].push({ type: fnName, @@ -143,27 +160,30 @@ var ReactDefaultPerf = { 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({ + ReactDefaultPerf._allMeasurements.push(entry = { exclusive: {}, inclusive: {}, render: {}, counts: {}, writes: {}, displayNames: {}, + hierarchy: {}, totalTime: 0, created: {}, }); start = performanceNow(); rv = func.apply(this, args); - ReactDefaultPerf._allMeasurements[ - ReactDefaultPerf._allMeasurements.length - 1 - ].totalTime = performanceNow() - start; + entry.totalTime = performanceNow() - start; return rv; } else if (fnName === '_mountImageIntoNode' || moduleName === 'ReactDOMIDOperations' || @@ -228,16 +248,11 @@ var ReactDefaultPerf = { return func.apply(this, args); } - var rootNodeID = fnName === 'mountComponent' ? - args[0] : - this._rootNodeID; + var rootNodeID = getIDOfComposite(this); var isRender = fnName === '_renderValidatedComponent'; var isMount = fnName === 'mountComponent'; var mountStack = ReactDefaultPerf._mountStack; - var entry = ReactDefaultPerf._allMeasurements[ - ReactDefaultPerf._allMeasurements.length - 1 - ]; if (isRender) { addValue(entry.counts, rootNodeID, 1); @@ -246,10 +261,14 @@ var ReactDefaultPerf = { 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) { @@ -269,6 +288,16 @@ var ReactDefaultPerf = { }; return rv; + } else if ( + (moduleName === 'ReactDOMComponent' || + moduleName === 'ReactDOMTextComponent') && + (fnName === 'mountComponent' || + fnName === 'receiveComponent')) { + + rv = func.apply(this, args); + entry.hierarchy[this._rootNodeID] = + ReactDefaultPerf._compositeStack.slice(); + return rv; } else { return func.apply(this, args); } diff --git a/src/test/ReactDefaultPerfAnalysis.js b/src/test/ReactDefaultPerfAnalysis.js index 4806940b7d6cb..0134a839ae29f 100644 --- a/src/test/ReactDefaultPerfAnalysis.js +++ b/src/test/ReactDefaultPerfAnalysis.js @@ -173,18 +173,23 @@ function getUnchangedComponents(measurement) { // 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 dirtyLeafIDs = Object.keys(measurement.writes); + var writes = measurement.writes; + 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 !== '') { + measurement.hierarchy[id].forEach((c) => dirtyComposites[c] = true); + } + }); + }); var allIDs = assign({}, measurement.exclusive, measurement.inclusive); for (var id in allIDs) { var isDirty = false; - // For each component that rendered, see if a component that triggered - // a DOM op is in its subtree. - for (var i = 0; i < dirtyLeafIDs.length; i++) { - if (dirtyLeafIDs[i].indexOf(id) === 0) { - isDirty = true; - break; - } + // 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]) {