diff options
Diffstat (limited to 'node_modules/react-dom/lib/ReactPerf.js')
-rw-r--r-- | node_modules/react-dom/lib/ReactPerf.js | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/node_modules/react-dom/lib/ReactPerf.js b/node_modules/react-dom/lib/ReactPerf.js new file mode 100644 index 000000000..a7e1201bc --- /dev/null +++ b/node_modules/react-dom/lib/ReactPerf.js @@ -0,0 +1,500 @@ +/** + * Copyright 2016-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. + * + * + */ + +'use strict'; + +var _assign = require('object-assign'); + +var _extends = _assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; + +var ReactDebugTool = require('./ReactDebugTool'); +var warning = require('fbjs/lib/warning'); +var alreadyWarned = false; + +function roundFloat(val) { + var base = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2; + + var n = Math.pow(10, base); + return Math.floor(val * n) / n; +} + +// Flow type definition of console.table is too strict right now, see +// https://github.com/facebook/flow/pull/2353 for updates +function consoleTable(table) { + console.table(table); +} + +function warnInProduction() { + if (alreadyWarned) { + return; + } + alreadyWarned = true; + if (typeof console !== 'undefined') { + console.error('ReactPerf is not supported in the production builds of React. ' + 'To collect measurements, please use the development build of React instead.'); + } +} + +function getLastMeasurements() { + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return []; + } + + return ReactDebugTool.getFlushHistory(); +} + +function getExclusive() { + var flushHistory = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getLastMeasurements(); + + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return []; + } + + var aggregatedStats = {}; + var affectedIDs = {}; + + function updateAggregatedStats(treeSnapshot, instanceID, timerType, applyUpdate) { + var displayName = treeSnapshot[instanceID].displayName; + + var key = displayName; + var stats = aggregatedStats[key]; + if (!stats) { + affectedIDs[key] = {}; + stats = aggregatedStats[key] = { + key: key, + instanceCount: 0, + counts: {}, + durations: {}, + totalDuration: 0 + }; + } + if (!stats.durations[timerType]) { + stats.durations[timerType] = 0; + } + if (!stats.counts[timerType]) { + stats.counts[timerType] = 0; + } + affectedIDs[key][instanceID] = true; + applyUpdate(stats); + } + + flushHistory.forEach(function (flush) { + var measurements = flush.measurements, + treeSnapshot = flush.treeSnapshot; + + measurements.forEach(function (measurement) { + var duration = measurement.duration, + instanceID = measurement.instanceID, + timerType = measurement.timerType; + + updateAggregatedStats(treeSnapshot, instanceID, timerType, function (stats) { + stats.totalDuration += duration; + stats.durations[timerType] += duration; + stats.counts[timerType]++; + }); + }); + }); + + return Object.keys(aggregatedStats).map(function (key) { + return _extends({}, aggregatedStats[key], { + instanceCount: Object.keys(affectedIDs[key]).length + }); + }).sort(function (a, b) { + return b.totalDuration - a.totalDuration; + }); +} + +function getInclusive() { + var flushHistory = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getLastMeasurements(); + + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return []; + } + + var aggregatedStats = {}; + var affectedIDs = {}; + + function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) { + var _treeSnapshot$instanc = treeSnapshot[instanceID], + displayName = _treeSnapshot$instanc.displayName, + ownerID = _treeSnapshot$instanc.ownerID; + + var owner = treeSnapshot[ownerID]; + var key = (owner ? owner.displayName + ' > ' : '') + displayName; + var stats = aggregatedStats[key]; + if (!stats) { + affectedIDs[key] = {}; + stats = aggregatedStats[key] = { + key: key, + instanceCount: 0, + inclusiveRenderDuration: 0, + renderCount: 0 + }; + } + affectedIDs[key][instanceID] = true; + applyUpdate(stats); + } + + var isCompositeByID = {}; + flushHistory.forEach(function (flush) { + var measurements = flush.measurements; + + measurements.forEach(function (measurement) { + var instanceID = measurement.instanceID, + timerType = measurement.timerType; + + if (timerType !== 'render') { + return; + } + isCompositeByID[instanceID] = true; + }); + }); + + flushHistory.forEach(function (flush) { + var measurements = flush.measurements, + treeSnapshot = flush.treeSnapshot; + + measurements.forEach(function (measurement) { + var duration = measurement.duration, + instanceID = measurement.instanceID, + timerType = measurement.timerType; + + if (timerType !== 'render') { + return; + } + updateAggregatedStats(treeSnapshot, instanceID, function (stats) { + stats.renderCount++; + }); + var nextParentID = instanceID; + while (nextParentID) { + // As we traverse parents, only count inclusive time towards composites. + // We know something is a composite if its render() was called. + if (isCompositeByID[nextParentID]) { + updateAggregatedStats(treeSnapshot, nextParentID, function (stats) { + stats.inclusiveRenderDuration += duration; + }); + } + nextParentID = treeSnapshot[nextParentID].parentID; + } + }); + }); + + return Object.keys(aggregatedStats).map(function (key) { + return _extends({}, aggregatedStats[key], { + instanceCount: Object.keys(affectedIDs[key]).length + }); + }).sort(function (a, b) { + return b.inclusiveRenderDuration - a.inclusiveRenderDuration; + }); +} + +function getWasted() { + var flushHistory = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getLastMeasurements(); + + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return []; + } + + var aggregatedStats = {}; + var affectedIDs = {}; + + function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) { + var _treeSnapshot$instanc2 = treeSnapshot[instanceID], + displayName = _treeSnapshot$instanc2.displayName, + ownerID = _treeSnapshot$instanc2.ownerID; + + var owner = treeSnapshot[ownerID]; + var key = (owner ? owner.displayName + ' > ' : '') + displayName; + var stats = aggregatedStats[key]; + if (!stats) { + affectedIDs[key] = {}; + stats = aggregatedStats[key] = { + key: key, + instanceCount: 0, + inclusiveRenderDuration: 0, + renderCount: 0 + }; + } + affectedIDs[key][instanceID] = true; + applyUpdate(stats); + } + + flushHistory.forEach(function (flush) { + var measurements = flush.measurements, + treeSnapshot = flush.treeSnapshot, + operations = flush.operations; + + var isDefinitelyNotWastedByID = {}; + + // Find host components associated with an operation in this batch. + // Mark all components in their parent tree as definitely not wasted. + operations.forEach(function (operation) { + var instanceID = operation.instanceID; + + var nextParentID = instanceID; + while (nextParentID) { + isDefinitelyNotWastedByID[nextParentID] = true; + nextParentID = treeSnapshot[nextParentID].parentID; + } + }); + + // Find composite components that rendered in this batch. + // These are potential candidates for being wasted renders. + var renderedCompositeIDs = {}; + measurements.forEach(function (measurement) { + var instanceID = measurement.instanceID, + timerType = measurement.timerType; + + if (timerType !== 'render') { + return; + } + renderedCompositeIDs[instanceID] = true; + }); + + measurements.forEach(function (measurement) { + var duration = measurement.duration, + instanceID = measurement.instanceID, + timerType = measurement.timerType; + + if (timerType !== 'render') { + return; + } + + // If there was a DOM update below this component, or it has just been + // mounted, its render() is not considered wasted. + var updateCount = treeSnapshot[instanceID].updateCount; + + if (isDefinitelyNotWastedByID[instanceID] || updateCount === 0) { + return; + } + + // We consider this render() wasted. + updateAggregatedStats(treeSnapshot, instanceID, function (stats) { + stats.renderCount++; + }); + + var nextParentID = instanceID; + while (nextParentID) { + // Any parents rendered during this batch are considered wasted + // unless we previously marked them as dirty. + var isWasted = renderedCompositeIDs[nextParentID] && !isDefinitelyNotWastedByID[nextParentID]; + if (isWasted) { + updateAggregatedStats(treeSnapshot, nextParentID, function (stats) { + stats.inclusiveRenderDuration += duration; + }); + } + nextParentID = treeSnapshot[nextParentID].parentID; + } + }); + }); + + return Object.keys(aggregatedStats).map(function (key) { + return _extends({}, aggregatedStats[key], { + instanceCount: Object.keys(affectedIDs[key]).length + }); + }).sort(function (a, b) { + return b.inclusiveRenderDuration - a.inclusiveRenderDuration; + }); +} + +function getOperations() { + var flushHistory = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getLastMeasurements(); + + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return []; + } + + var stats = []; + flushHistory.forEach(function (flush, flushIndex) { + var operations = flush.operations, + treeSnapshot = flush.treeSnapshot; + + operations.forEach(function (operation) { + var instanceID = operation.instanceID, + type = operation.type, + payload = operation.payload; + var _treeSnapshot$instanc3 = treeSnapshot[instanceID], + displayName = _treeSnapshot$instanc3.displayName, + ownerID = _treeSnapshot$instanc3.ownerID; + + var owner = treeSnapshot[ownerID]; + var key = (owner ? owner.displayName + ' > ' : '') + displayName; + + stats.push({ + flushIndex: flushIndex, + instanceID: instanceID, + key: key, + type: type, + ownerID: ownerID, + payload: payload + }); + }); + }); + return stats; +} + +function printExclusive(flushHistory) { + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return; + } + + var stats = getExclusive(flushHistory); + var table = stats.map(function (item) { + var key = item.key, + instanceCount = item.instanceCount, + totalDuration = item.totalDuration; + + var renderCount = item.counts.render || 0; + var renderDuration = item.durations.render || 0; + return { + 'Component': key, + 'Total time (ms)': roundFloat(totalDuration), + 'Instance count': instanceCount, + 'Total render time (ms)': roundFloat(renderDuration), + 'Average render time (ms)': renderCount ? roundFloat(renderDuration / renderCount) : undefined, + 'Render count': renderCount, + 'Total lifecycle time (ms)': roundFloat(totalDuration - renderDuration) + }; + }); + consoleTable(table); +} + +function printInclusive(flushHistory) { + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return; + } + + var stats = getInclusive(flushHistory); + var table = stats.map(function (item) { + var key = item.key, + instanceCount = item.instanceCount, + inclusiveRenderDuration = item.inclusiveRenderDuration, + renderCount = item.renderCount; + + return { + 'Owner > Component': key, + 'Inclusive render time (ms)': roundFloat(inclusiveRenderDuration), + 'Instance count': instanceCount, + 'Render count': renderCount + }; + }); + consoleTable(table); +} + +function printWasted(flushHistory) { + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return; + } + + var stats = getWasted(flushHistory); + var table = stats.map(function (item) { + var key = item.key, + instanceCount = item.instanceCount, + inclusiveRenderDuration = item.inclusiveRenderDuration, + renderCount = item.renderCount; + + return { + 'Owner > Component': key, + 'Inclusive wasted time (ms)': roundFloat(inclusiveRenderDuration), + 'Instance count': instanceCount, + 'Render count': renderCount + }; + }); + consoleTable(table); +} + +function printOperations(flushHistory) { + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return; + } + + var stats = getOperations(flushHistory); + var table = stats.map(function (stat) { + return { + 'Owner > Node': stat.key, + 'Operation': stat.type, + 'Payload': typeof stat.payload === 'object' ? JSON.stringify(stat.payload) : stat.payload, + 'Flush index': stat.flushIndex, + 'Owner Component ID': stat.ownerID, + 'DOM Component ID': stat.instanceID + }; + }); + consoleTable(table); +} + +var warnedAboutPrintDOM = false; +function printDOM(measurements) { + process.env.NODE_ENV !== 'production' ? warning(warnedAboutPrintDOM, '`ReactPerf.printDOM(...)` is deprecated. Use ' + '`ReactPerf.printOperations(...)` instead.') : void 0; + warnedAboutPrintDOM = true; + return printOperations(measurements); +} + +var warnedAboutGetMeasurementsSummaryMap = false; +function getMeasurementsSummaryMap(measurements) { + process.env.NODE_ENV !== 'production' ? warning(warnedAboutGetMeasurementsSummaryMap, '`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' + '`ReactPerf.getWasted(...)` instead.') : void 0; + warnedAboutGetMeasurementsSummaryMap = true; + return getWasted(measurements); +} + +function start() { + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return; + } + + ReactDebugTool.beginProfiling(); +} + +function stop() { + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return; + } + + ReactDebugTool.endProfiling(); +} + +function isRunning() { + if (!(process.env.NODE_ENV !== 'production')) { + warnInProduction(); + return false; + } + + return ReactDebugTool.isProfiling(); +} + +var ReactPerfAnalysis = { + getLastMeasurements: getLastMeasurements, + getExclusive: getExclusive, + getInclusive: getInclusive, + getWasted: getWasted, + getOperations: getOperations, + printExclusive: printExclusive, + printInclusive: printInclusive, + printWasted: printWasted, + printOperations: printOperations, + start: start, + stop: stop, + isRunning: isRunning, + // Deprecated: + printDOM: printDOM, + getMeasurementsSummaryMap: getMeasurementsSummaryMap +}; + +module.exports = ReactPerfAnalysis;
\ No newline at end of file |