From e11e0465f4c13538a0c3acc0802d221dfabe4ea5 Mon Sep 17 00:00:00 2001 From: petehunt Date: Fri, 24 Jan 2014 00:37:51 -0800 Subject: [PATCH] New ReactDefaultPerf --- examples/todomvc-director/index.html | 5 +- src/ReactWithAddons.js | 4 + src/core/ReactCompositeComponent.js | 46 ++- src/core/ReactDOMIDOperations.js | 37 +- src/core/ReactDefaultInjection.js | 4 - src/core/ReactUpdates.js | 6 +- src/test/ReactDefaultPerf.js | 550 +++++++++------------------ src/test/ReactPerf.js | 11 +- 8 files changed, 248 insertions(+), 415 deletions(-) diff --git a/examples/todomvc-director/index.html b/examples/todomvc-director/index.html index a1221fd992845..bf1c18c497d6a 100644 --- a/examples/todomvc-director/index.html +++ b/examples/todomvc-director/index.html @@ -12,10 +12,11 @@
- - + + + diff --git a/src/ReactWithAddons.js b/src/ReactWithAddons.js index 3b92d59f813ac..ceac65bcf5c71 100644 --- a/src/ReactWithAddons.js +++ b/src/ReactWithAddons.js @@ -37,5 +37,9 @@ React.addons = { TransitionGroup: ReactTransitionGroup }; +if (__DEV__) { + React.addons.Perf = require('ReactDefaultPerf'); +} + module.exports = React; diff --git a/src/core/ReactCompositeComponent.js b/src/core/ReactCompositeComponent.js index 54f3a2b0f8521..84ce39778711b 100644 --- a/src/core/ReactCompositeComponent.js +++ b/src/core/ReactCompositeComponent.js @@ -1150,28 +1150,32 @@ var ReactCompositeComponentMixin = { /** * @private */ - _renderValidatedComponent: function() { - var renderedComponent; - var previousContext = ReactContext.current; - ReactContext.current = this._processChildContext(this._currentContext); - ReactCurrentOwner.current = this; - try { - renderedComponent = this.render(); - } catch (error) { - // IE8 requires `catch` in order to use `finally`. - throw error; - } finally { - ReactContext.current = previousContext; - ReactCurrentOwner.current = null; + _renderValidatedComponent: ReactPerf.measure( + 'ReactCompositeComponent', + '_renderValidatedComponent', + function() { + var renderedComponent; + var previousContext = ReactContext.current; + ReactContext.current = this._processChildContext(this._currentContext); + ReactCurrentOwner.current = this; + try { + renderedComponent = this.render(); + } catch (error) { + // IE8 requires `catch` in order to use `finally`. + throw error; + } finally { + ReactContext.current = previousContext; + ReactCurrentOwner.current = null; + } + invariant( + ReactComponent.isValidComponent(renderedComponent), + '%s.render(): A valid ReactComponent must be returned. You may have ' + + 'returned null, undefined, an array, or some other invalid object.', + this.constructor.displayName || 'ReactCompositeComponent' + ); + return renderedComponent; } - invariant( - ReactComponent.isValidComponent(renderedComponent), - '%s.render(): A valid ReactComponent must be returned. You may have ' + - 'returned null, undefined, an array, or some other invalid object.', - this.constructor.displayName || 'ReactCompositeComponent' - ); - return renderedComponent; - }, + ), /** * @private diff --git a/src/core/ReactDOMIDOperations.js b/src/core/ReactDOMIDOperations.js index 2a89f7ec91590..f15d7c5416674 100644 --- a/src/core/ReactDOMIDOperations.js +++ b/src/core/ReactDOMIDOperations.js @@ -25,6 +25,7 @@ var CSSPropertyOperations = require('CSSPropertyOperations'); var DOMChildrenOperations = require('DOMChildrenOperations'); var DOMPropertyOperations = require('DOMPropertyOperations'); var ReactMount = require('ReactMount'); +var ReactPerf = require('ReactPerf'); var getTextContentAccessor = require('getTextContentAccessor'); var invariant = require('invariant'); @@ -66,7 +67,7 @@ var ReactDOMIDOperations = { * @param {*} value New value of the property. * @internal */ - updatePropertyByID: function(id, name, value) { + updatePropertyByID: ReactPerf.measure('ReactDOMIDOperations', 'updatePropertyByID', function(id, name, value) { var node = ReactMount.getNode(id); invariant( !INVALID_PROPERTY_ERRORS.hasOwnProperty(name), @@ -82,7 +83,7 @@ var ReactDOMIDOperations = { } else { DOMPropertyOperations.deleteValueForProperty(node, name); } - }, + }), /** * Updates a DOM node to remove a property. This should only be used to remove @@ -92,7 +93,7 @@ var ReactDOMIDOperations = { * @param {string} name A property name to remove, see `DOMProperty`. * @internal */ - deletePropertyByID: function(id, name, value) { + deletePropertyByID: ReactPerf.measure('ReactDOMIDOperations', 'deletePropertyByID', function(id, name, value) { var node = ReactMount.getNode(id); invariant( !INVALID_PROPERTY_ERRORS.hasOwnProperty(name), @@ -100,7 +101,7 @@ var ReactDOMIDOperations = { INVALID_PROPERTY_ERRORS[name] ); DOMPropertyOperations.deleteValueForProperty(node, name, value); - }, + }), /** * Updates a DOM node with new style values. If a value is specified as '', @@ -110,10 +111,10 @@ var ReactDOMIDOperations = { * @param {object} styles Mapping from styles to values. * @internal */ - updateStylesByID: function(id, styles) { + updateStylesByID: ReactPerf.measure('ReactDOMIDOperations', 'updateStylesByID', function(id, styles) { var node = ReactMount.getNode(id); CSSPropertyOperations.setValueForStyles(node, styles); - }, + }), /** * Updates a DOM node's innerHTML. @@ -122,21 +123,21 @@ var ReactDOMIDOperations = { * @param {string} html An HTML string. * @internal */ - updateInnerHTMLByID: function(id, html) { + updateInnerHTMLByID: ReactPerf.measure('ReactDOMIDOperations', 'updateInnerHTMLByID', function(id, html) { var node = ReactMount.getNode(id); - + // IE8: When updating a just created node with innerHTML only leading // whitespace is removed. When updating an existing node with innerHTML // whitespace in root TextNodes is also collapsed. // @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html - + if (useWhitespaceWorkaround === undefined) { // Feature detection; only IE8 is known to behave improperly like this. var temp = document.createElement('div'); temp.innerHTML = ' '; useWhitespaceWorkaround = temp.innerHTML === ''; } - + if (useWhitespaceWorkaround) { // Magic theory: IE8 supposedly differentiates between added and updated // nodes when processing innerHTML, innerHTML on updated nodes suffers @@ -144,7 +145,7 @@ var ReactDOMIDOperations = { // the initial and more favorable whitespace behavior. node.parentNode.replaceChild(node, node); } - + if (useWhitespaceWorkaround && html.match(/^[ \r\n\t\f]/)) { // Recover leading whitespace by temporarily prepending any character. // \uFEFF has the potential advantage of being zero-width/invisible. @@ -153,7 +154,7 @@ var ReactDOMIDOperations = { } else { node.innerHTML = html; } - }, + }), /** * Updates a DOM node's text content set by `props.content`. @@ -162,10 +163,10 @@ var ReactDOMIDOperations = { * @param {string} content Text content. * @internal */ - updateTextContentByID: function(id, content) { + updateTextContentByID: ReactPerf.measure('ReactDOMIDOperations', 'updateTextContentByID', function(id, content) { var node = ReactMount.getNode(id); node[textContentAccessor] = content; - }, + }), /** * Replaces a DOM node that exists in the document with markup. @@ -175,10 +176,10 @@ var ReactDOMIDOperations = { * @internal * @see {Danger.dangerouslyReplaceNodeWithMarkup} */ - dangerouslyReplaceNodeWithMarkupByID: function(id, markup) { + dangerouslyReplaceNodeWithMarkupByID: ReactPerf.measure('ReactDOMIDOperations', 'dangerouslyReplaceNodeWithMarkupByID', function(id, markup) { var node = ReactMount.getNode(id); DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup(node, markup); - }, + }), /** * Updates a component's children by processing a series of updates. @@ -187,12 +188,12 @@ var ReactDOMIDOperations = { * @param {array} markup List of markup strings. * @internal */ - dangerouslyProcessChildrenUpdates: function(updates, markup) { + dangerouslyProcessChildrenUpdates: ReactPerf.measure('ReactDOMIDOperations', 'dangerouslyProcessChildrenUpdates', function(updates, markup) { for (var i = 0; i < updates.length; i++) { updates[i].parentNode = ReactMount.getNode(updates[i].parentID); } DOMChildrenOperations.processUpdates(updates, markup); - } + }) }; diff --git a/src/core/ReactDefaultInjection.js b/src/core/ReactDefaultInjection.js index 773d8c015d20b..450150ce43f76 100644 --- a/src/core/ReactDefaultInjection.js +++ b/src/core/ReactDefaultInjection.js @@ -93,10 +93,6 @@ function inject() { DOMProperty.injection.injectDOMPropertyConfig(DefaultDOMPropertyConfig); - if (__DEV__) { - ReactPerf.injection.injectMeasure(require('ReactDefaultPerf').measure); - } - ReactUpdates.injection.injectBatchingStrategy( ReactDefaultBatchingStrategy ); diff --git a/src/core/ReactUpdates.js b/src/core/ReactUpdates.js index c1d92e1f2929e..cd69b9df4d594 100644 --- a/src/core/ReactUpdates.js +++ b/src/core/ReactUpdates.js @@ -18,6 +18,8 @@ "use strict"; +var ReactPerf = require('ReactPerf'); + var invariant = require('invariant'); var dirtyComponents = []; @@ -75,7 +77,7 @@ function clearDirtyComponents() { dirtyComponents.length = 0; } -function flushBatchedUpdates() { +var flushBatchedUpdates = ReactPerf.measure('ReactUpdates', 'flushBatchedUpdates', function() { // Run these in separate functions so the JIT can optimize try { runBatchedUpdates(); @@ -85,7 +87,7 @@ function flushBatchedUpdates() { } finally { clearDirtyComponents(); } -} +}); /** * Mark a component as needing a rerender, adding an optional callback to a diff --git a/src/test/ReactDefaultPerf.js b/src/test/ReactDefaultPerf.js index 8ae38d573c5ce..815546c530ef4 100644 --- a/src/test/ReactDefaultPerf.js +++ b/src/test/ReactDefaultPerf.js @@ -17,391 +17,219 @@ * @typechecks static-only */ -"use strict"; +var ReactPerf = require('ReactPerf'); var performanceNow = require('performanceNow'); -var ReactDefaultPerf = {}; - -if (__DEV__) { - ReactDefaultPerf = { - /** - * Gets the stored information for a given object's function. - * - * @param {string} objName - * @param {string} fnName - * @return {?object} - */ - getInfo: function(objName, fnName) { - if (!this.info[objName] || !this.info[objName][fnName]) { - return null; - } - return this.info[objName][fnName]; - }, - - /** - * Gets the logs pertaining to a given object's function. - * - * @param {string} objName - * @param {string} fnName - * @return {?array} - */ - getLogs: function(objName, fnName) { - if (!this.getInfo(objName, fnName)) { - return null; - } - return this.logs.filter(function(log) { - return log.objName === objName && log.fnName === fnName; - }); - }, - - /** - * Runs through the logs and builds an array of arrays, where each array - * walks through the mounting/updating of each component underneath. - * - * @param {string} rootID The reactID of the root node, e.g. '.r[2cpyq]' - * @return {array} - */ - getRawRenderHistory: function(rootID) { - var history = []; - /** - * Since logs are added after the method returns, the logs are in a sense - * upside-down: the inner-most elements from mounting/updating are logged - * first, and the last addition to the log is the top renderComponent. - * Therefore, we flip the logs upside down for ease of processing, and - * reverse the history array at the end so the earliest event has index 0. - */ - var logs = this.logs.filter(function(log) { - return log.reactID.indexOf(rootID) === 0; - }).reverse(); - - var subHistory = []; - logs.forEach(function(log, i) { - if (i && log.reactID === rootID && logs[i - 1].reactID !== rootID) { - subHistory.length && history.push(subHistory); - subHistory = []; - } - subHistory.push(log); - }); - if (subHistory.length) { - history.push(subHistory); - } - return history.reverse(); - }, - - /** - * Runs through the logs and builds an array of strings, where each string - * is a multiline formatted way of walking through the mounting/updating - * underneath. - * - * @param {string} rootID The reactID of the root node, e.g. '.r[2cpyq]' - * @return {array} - */ - getRenderHistory: function(rootID) { - var history = this.getRawRenderHistory(rootID); - - return history.map(function(subHistory) { - var headerString = ( - 'log# Component (execution time) [bloat from logging]\n' + - '================================================================\n' - ); - return headerString + subHistory.map(function(log) { - // Add two spaces for every layer in the reactID. - var indents = '\t' + Array(log.reactID.split('.[').length).join(' '); - var delta = _microTime(log.timing.delta); - var bloat = _microTime(log.timing.timeToLog); - - return log.index + indents + log.name + ' (' + delta + 'ms)' + - ' [' + bloat + 'ms]'; - }).join('\n'); - }); - }, - - /** - * Print the render history from `getRenderHistory` using console.log. - * This is currently the best way to display perf data from - * any React component; working on that. - * - * @param {string} rootID The reactID of the root node, e.g. '.r[2cpyq]' - * @param {number} index - */ - printRenderHistory: function(rootID, index) { - var history = this.getRenderHistory(rootID); - if (!history[index]) { - console.warn( - 'Index', index, 'isn\'t available! ' + - 'The render history is', history.length, 'long.' - ); - return; +// Don't try to save users less than 1.2ms (a number I made up) +var DONT_CARE_THRESHOLD = 1.2; + +function getCleanComponents(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 dirtyLeafIDs = Object.keys(measurement.writes); + for (var id in measurement.inclusive) { + var isDirty = false; + // For each component that rendered, see if a component that triggerd + // a DOM op is in its subtree. + for (var i = 0; i < dirtyLeafIDs.length; i++) { + if (dirtyLeafIDs[i].indexOf(id) === 0) { + isDirty = true; + break; } - console.log( - 'Loading render history #' + (index + 1) + - ' of ' + history.length + ':\n' + history[index] - ); - }, - - /** - * Prints the heatmap legend to console, showing how the colors correspond - * with render times. This relies on console.log styles. - */ - printHeatmapLegend: function() { - if (!this.options.heatmap.enabled) { - return; - } - var max = this.info.React - && this.info.React.renderComponent - && this.info.React.renderComponent.max; - if (max) { - var logStr = 'Heatmap: '; - for (var ii = 0; ii <= 10 * max; ii += max) { - logStr += '%c ' + (Math.round(ii) / 10) + 'ms '; - } - console.log( - logStr, - 'background-color: hsla(100, 100%, 50%, 0.6);', - 'background-color: hsla( 90, 100%, 50%, 0.6);', - 'background-color: hsla( 80, 100%, 50%, 0.6);', - 'background-color: hsla( 70, 100%, 50%, 0.6);', - 'background-color: hsla( 60, 100%, 50%, 0.6);', - 'background-color: hsla( 50, 100%, 50%, 0.6);', - 'background-color: hsla( 40, 100%, 50%, 0.6);', - 'background-color: hsla( 30, 100%, 50%, 0.6);', - 'background-color: hsla( 20, 100%, 50%, 0.6);', - 'background-color: hsla( 10, 100%, 50%, 0.6);', - 'background-color: hsla( 0, 100%, 50%, 0.6);' - ); - } - }, - - /** - * Measure a given function with logging information, and calls a callback - * if there is one. - * - * @param {string} objName - * @param {string} fnName - * @param {function} func - * @return {function} - */ - measure: function(objName, fnName, func) { - var info = _getNewInfo(objName, fnName); + } + if (!isDirty) { + cleanComponents[id] = measurement.inclusive[id]; + } + } + return cleanComponents; +} - var fnArgs = _getFnArguments(func); +function getSortedAdviceCandidates(measurements) { + var candidates = {}; - return function(...args) { - var timeBeforeFn = performanceNow(); - var fnReturn = func.apply(this, args); - var timeAfterFn = performanceNow(); + // First aggregate all measurements by class name + for (var i = 0; i < measurements.length; i++) { + var cleanComponents = getCleanComponents(measurements[i]); + for (var id in cleanComponents) { + var key = measurements[i].displayNames[id] || '(unknown)'; + candidates[key] = (candidates[key] || 0) + cleanComponents[id]; + } + } - /** - * Hold onto arguments in a readable way: args[1] -> args.component. - * args is also passed to the callback, so if you want to save an - * argument in the log, do so in the callback. - */ - var argsObject = {}; - for (var i = 0; i < args.length; i++) { - argsObject[fnArgs[i]] = args[i]; - } + // Now make a sorted array with the results. + var arr = []; + for (key in candidates) { + if (candidates[key] < DONT_CARE_THRESHOLD) { + continue; + } - var log = { - index: ReactDefaultPerf.logs.length, - fnName: fnName, - objName: objName, - timing: { - before: timeBeforeFn, - after: timeAfterFn, - delta: timeAfterFn - timeBeforeFn - } - }; + arr.push({ + componentName: key, + time: candidates[key] + }); + } - ReactDefaultPerf.logs.push(log); + arr.sort(function(a, b) { + return b.time - a.time; + }); - /** - * The callback gets: - * - this (the component) - * - the original method's arguments - * - what the method returned - * - the log object, and - * - the wrapped method's info object. - */ - var callback = _getCallback(objName, fnName); - callback && callback(this, argsObject, fnReturn, log, info); + return arr; +} - log.timing.timeToLog = performanceNow() - timeAfterFn; +function getSortedRenderMethods(measurements) { + var candidates = {}; + for (var i = 0; i < measurements.length; i++) { + var measurement = measurements[i]; + for (var id in measurement.exclusive) { + var displayName = measurement.displayNames[id]; + candidates[displayName] = candidates[displayName] || 0; + candidates[displayName] += measurement.exclusive[id]; + } + } - return fnReturn; - }; - }, + // Now make a sorted array with the results. + var arr = []; + for (var displayName in candidates) { + if (candidates[displayName] < DONT_CARE_THRESHOLD) { + continue; + } + arr.push({ + componentName: displayName, + time: candidates[displayName] + }); + } - /** - * Holds information on wrapped objects/methods. - * For instance, ReactDefaultPerf.info.React.renderComponent - */ - info: {}, + arr.sort(function(a, b) { + return b.time - a.time; + }); - /** - * Holds all of the logs. Filter this to pull desired information. - */ - logs: [], + return arr; +} - /** - * Toggle settings for ReactDefaultPerf - */ - options: { - /** - * The heatmap sets the background color of the React containers - * according to how much total time has been spent rendering them. - * The most temporally expensive component is set as pure red, - * and the others are colored from green to red as a fraction - * of that max component time. - */ - heatmap: { - enabled: true - } - } - }; +var ReactDefaultPerf = { + _allMeasurements: null, // last item in the list is the current one + _injected: false, - /** - * Gets a info area for a given object's function, adding a new one if - * necessary. - * - * @param {string} objName - * @param {string} fnName - * @return {object} - */ - var _getNewInfo = function(objName, fnName) { - var info = ReactDefaultPerf.getInfo(objName, fnName); - if (info) { - return info; + start: function() { + if (!ReactDefaultPerf._injected) { + ReactPerf.injection.injectMeasure(ReactDefaultPerf.measure); } - ReactDefaultPerf.info[objName] = ReactDefaultPerf.info[objName] || {}; - return ReactDefaultPerf.info[objName][fnName] = { - getLogs: function() { - return ReactDefaultPerf.getLogs(objName, fnName); - } - }; - }; - - /** - * Gets a list of the argument names from a function's definition. - * This is useful for storing arguments by their names within wrapFn(). - * - * @param {function} fn - * @return {array} - */ - var _getFnArguments = function(fn) { - var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; - var fnStr = fn.toString().replace(STRIP_COMMENTS, ''); - fnStr = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')); - return fnStr.match(/([^\s,]+)/g); - }; - - /** - * Store common callbacks within ReactDefaultPerf. - * - * @param {string} objName - * @param {string} fnName - * @return {?function} - */ - var _getCallback = function(objName, fnName) { - switch (objName + '.' + fnName) { - case 'React.renderComponent': - return _renderComponentCallback; - case 'ReactDOMComponent.mountComponent': - case 'ReactDOMComponent.updateComponent': - return _nativeComponentCallback; - case 'ReactCompositeComponent.mountComponent': - case 'ReactCompositeComponent.updateComponent': - return _compositeComponentCallback; - default: - return null; + ReactDefaultPerf._allMeasurements = []; + ReactPerf.enableMeasure = true; + }, + + end: function() { + // Find the component with the highest exclusive time + // that was not written to. + ReactPerf.enableMeasure = false; + }, + + getLastMeasurements: function() { + return ReactDefaultPerf._allMeasurements; + }, + + printAdvice: function(measurements) { + measurements = measurements || ReactDefaultPerf._allMeasurements; + var candidates = getSortedAdviceCandidates(measurements); + if (candidates.length === 0) { + console.log('I have no performance advice for you at this time.'); + return; } - }; + console.log('I have identified', candidates.length, 'places where you could add shouldComponentUpdate():'); + console.table(candidates.map(function(candidate) { + return { + 'Component class name': candidate.componentName, + 'Time you could save': candidate.time.toFixed(2) + 'ms' + }; + })); + console.log( + 'tl;dr adding shouldComponentUpdate() to', + candidates[0].componentName, + 'could save you up to', + candidates[0].time.toFixed(2), + 'ms for the total interaction recorded.' + ); + console.log('For more information see https://gist.github.com/petehunt/8595248'); + }, + + printExpensiveRenderMethods: function(measurements) { + // TODO: i'm not sure if this is the best way to help out the user. + measurements = measurements || ReactDefaultPerf._allMeasurements; + var renderMethods = getSortedRenderMethods(measurements); + console.log('The top', renderMethods.length, 'render() methods are:'); + console.table(renderMethods.map(function(renderMethod) { + return { + 'Component class name': renderMethod.componentName, + 'Time it took (exclusive)': renderMethod.time.toFixed(2) + 'ms' + }; + })); + console.log('This data may or may not be actionable. Try ReactDefaultPerf.printAdvice() first before trying to optimize render() methods.'); + }, + + measure: function(objName, fnName, func) { + return function(...args) { + if (fnName === 'flushBatchedUpdates') { + ReactDefaultPerf._allMeasurements.push({ + exclusive: {}, + inclusive: {}, + counts: {}, + writes: {}, + displayNames: {} + }); + return func.apply(this, args); + } else if (objName === 'ReactDOMIDOperations') { + var start = performanceNow(); + var rv = func.apply(this, args); + var totalTime = performanceNow() - start; + var writes = + ReactDefaultPerf._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1] + .writes; + + if (fnName === 'dangerouslyProcessChildrenUpdates') { + // special format + args[0].forEach(function(update) { + writes[update.parentID] = writes[update.parentID] || []; + writes[update.parentID].push({ + type: fnName, + time: totalTime + }); + }); + } else { + // basic format + writes[args[0]] = writes[args[0]] || []; + writes[args[0]].push({ + type: fnName, + time: totalTime + }); + } + return rv; + } else if (fnName === 'updateComponent' || fnName === '_renderValidatedComponent') { + var entry = ReactDefaultPerf._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1]; + if (fnName === 'updateComponent') { + // Don't double-count + entry.counts[this._rootNodeID] = entry.counts[this._rootNodeID] || 0; + entry.counts[this._rootNodeID] += 1; + } + var start = performanceNow(); + var rv = func.apply(this, args); + var totalTime = performanceNow() - start; - /** - * Callback function for React.renderComponent - * - * @param {object} component - * @param {object} args - * @param {?object} fnReturn - * @param {object} log - * @param {object} info - */ - var _renderComponentCallback = - function(component, args, fnReturn, log, info) { - log.name = args.nextComponent.constructor.displayName || '[unknown]'; - log.reactID = fnReturn._rootNodeID || null; + var typeOfLog = fnName === 'updateComponent' ? entry.inclusive : entry.exclusive; + typeOfLog[this._rootNodeID] = typeOfLog[this._rootNodeID] || 0; + typeOfLog[this._rootNodeID] += totalTime; - if (ReactDefaultPerf.options.heatmap.enabled) { - var container = args.container; - if (!container.loggedByReactDefaultPerf) { - container.loggedByReactDefaultPerf = true; - info.components = info.components || []; - info.components.push(container); - } + entry.displayNames[this._rootNodeID] = this.constructor.displayName; - container.count = container.count || 0; - container.count += log.timing.delta; - info.max = info.max || 0; - if (container.count > info.max) { - info.max = container.count; - info.components.forEach(function(component) { - _setHue(component, 100 - 100 * component.count / info.max); - }); + return rv; } else { - _setHue(container, 100 - 100 * container.count / info.max); + return func.apply(this, args); } - } - }; - - /** - * Callback function for ReactDOMComponent - * - * @param {object} component - * @param {object} args - * @param {?object} fnReturn - * @param {object} log - * @param {object} info - */ - var _nativeComponentCallback = - function(component, args, fnReturn, log, info) { - log.name = component.tagName || '[unknown]'; - log.reactID = component._rootNodeID; - }; - - /** - * Callback function for ReactCompositeComponent - * - * @param {object} component - * @param {object} args - * @param {?object} fnReturn - * @param {object} log - * @param {object} info - */ - var _compositeComponentCallback = - function(component, args, fnReturn, log, info) { - log.name = component.constructor.displayName || '[unknown]'; - log.reactID = component._rootNodeID; - }; - - /** - * Using the hsl() background-color attribute, colors an element. - * - * @param {DOMElement} el - * @param {number} hue [0 for red, 120 for green, 240 for blue] - */ - var _setHue = function(el, hue) { - el.style.backgroundColor = 'hsla(' + hue + ', 100%, 50%, 0.6)'; - }; - - /** - * Round to the thousandth place. - * @param {number} time - * @return {number} - */ - var _microTime = function(time) { - return Math.round(time * 1000) / 1000; - }; + }; + } } module.exports = ReactDefaultPerf; diff --git a/src/test/ReactPerf.js b/src/test/ReactPerf.js index 6516769710bc2..7e5bcc0d4a733 100644 --- a/src/test/ReactPerf.js +++ b/src/test/ReactPerf.js @@ -19,6 +19,10 @@ "use strict"; +/** + * ReactPerf is a general AOP system designed to measure performance. This module + * only has the hooks: see ReactDefaultPerf for a better tool. + */ var ReactPerf = { /** * Boolean to enable/disable measurement. Set to false by default to prevent @@ -66,13 +70,6 @@ var ReactPerf = { } }; -if (__DEV__) { - var ExecutionEnvironment = require('ExecutionEnvironment'); - var url = (ExecutionEnvironment.canUseDOM && window.location.href) || ''; - ReactPerf.enableMeasure = ReactPerf.enableMeasure || - (/[?&]react_perf\b/).test(url); -} - /** * Simply passes through the measured function, without measuring it. *