UNPKG

d2-ui

Version:
237 lines (208 loc) 8.63 kB
/** * Copyright 2013-2015, 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 * @typechecks static-only */ 'use strict'; var DOMProperty = require('./DOMProperty'); var ReactDefaultPerfAnalysis = require('./ReactDefaultPerfAnalysis'); var ReactMount = require('./ReactMount'); var ReactPerf = require('./ReactPerf'); var performanceNow = require('fbjs/lib/performanceNow'); function roundFloat(val) { return Math.floor(val * 100) / 100; } function addValue(obj, key, val) { obj[key] = (obj[key] || 0) + val; } var ReactDefaultPerf = { _allMeasurements: [], // last item in the list is the current one _mountStack: [0], _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 ReactDefaultPerf._allMeasurements; }, printExclusive: function (measurements) { measurements = 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 = 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) { 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 = measurements || ReactDefaultPerf._allMeasurements; console.table(ReactDefaultPerf.getMeasurementsSummaryMap(measurements)); console.log('Total time:', ReactDefaultPerfAnalysis.getTotalTime(measurements).toFixed(2) + ' ms'); }, printDOM: function (measurements) { measurements = 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); 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 writes = ReactDefaultPerf._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1].writes; writes[id] = writes[id] || []; writes[id].push({ type: fnName, time: totalTime, args: args }); }, measure: function (moduleName, fnName, func) { return function () { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var totalTime; var rv; var start; 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({ exclusive: {}, inclusive: {}, render: {}, counts: {}, writes: {}, displayNames: {}, totalTime: 0, created: {} }); start = performanceNow(); rv = func.apply(this, args); ReactDefaultPerf._allMeasurements[ReactDefaultPerf._allMeasurements.length - 1].totalTime = performanceNow() - start; return rv; } else if (fnName === '_mountImageIntoNode' || moduleName === 'ReactBrowserEventEmitter' || moduleName === 'ReactDOMIDOperations' || moduleName === 'CSSPropertyOperations' || moduleName === 'DOMChildrenOperations' || moduleName === 'DOMPropertyOperations') { start = performanceNow(); rv = func.apply(this, args); totalTime = performanceNow() - start; if (fnName === '_mountImageIntoNode') { var mountID = ReactMount.getID(args[1]); ReactDefaultPerf._recordWrite(mountID, fnName, totalTime, args[0]); } else if (fnName === 'dangerouslyProcessChildrenUpdates') { // special format args[0].forEach(function (update) { var writeArgs = {}; if (update.fromIndex !== null) { writeArgs.fromIndex = update.fromIndex; } if (update.toIndex !== null) { writeArgs.toIndex = update.toIndex; } if (update.textContent !== null) { writeArgs.textContent = update.textContent; } if (update.markupIndex !== null) { writeArgs.markup = args[1][update.markupIndex]; } ReactDefaultPerf._recordWrite(update.parentID, update.type, totalTime, writeArgs); }); } else { // basic format var id = args[0]; if (typeof id === 'object') { id = ReactMount.getID(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 = fnName === 'mountComponent' ? args[0] : this._rootNodeID; 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); } else if (isMount) { entry.created[rootNodeID] = true; mountStack.push(0); } start = performanceNow(); rv = func.apply(this, args); totalTime = performanceNow() - start; 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 { return func.apply(this, args); } }; } }; module.exports = ReactDefaultPerf;