UNPKG

react-addons

Version:

Simple packaging of react addons to avoid fiddly 'react/addons' npm module.

200 lines (172 loc) 5.77 kB
/** * Copyright 2013-2014 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * @providesModule ReactDefaultPerfAnalysis */ var merge = require("./merge"); // 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', TEXT_CONTENT: 'set textContent', 'updatePropertyByID': 'update attribute', 'deletePropertyByID': 'delete attribute', 'updateStylesByID': 'update styles', 'updateInnerHTMLByID': 'set innerHTML', 'dangerouslyReplaceNodeWithMarkupByID': '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 = []; for (var i = 0; i < measurements.length; i++) { var measurement = measurements[i]; var id; for (id in measurement.writes) { 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 = merge(measurement.exclusive, measurement.inclusive); for (var id in allIDs) { displayName = measurement.displayNames[id].current; candidates[displayName] = candidates[displayName] || { componentName: displayName, inclusive: 0, exclusive: 0, count: 0 }; 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 = merge(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 dirtyLeafIDs = Object.keys(measurement.writes); var allIDs = merge(measurement.exclusive, measurement.inclusive); for (var id in allIDs) { 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; } } if (!isDirty && measurement.counts[id] > 0) { cleanComponents[id] = true; } } return cleanComponents; } var ReactDefaultPerfAnalysis = { getExclusiveSummary: getExclusiveSummary, getInclusiveSummary: getInclusiveSummary, getDOMSummary: getDOMSummary, getTotalTime: getTotalTime }; module.exports = ReactDefaultPerfAnalysis;