1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | 'use strict';
|
11 |
|
12 | var _assign = require('object-assign');
|
13 |
|
14 | 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; };
|
15 |
|
16 | var ReactDebugTool = require('./ReactDebugTool');
|
17 | var lowPriorityWarning = require('./lowPriorityWarning');
|
18 | var alreadyWarned = false;
|
19 |
|
20 | function roundFloat(val) {
|
21 | var base = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
|
22 |
|
23 | var n = Math.pow(10, base);
|
24 | return Math.floor(val * n) / n;
|
25 | }
|
26 |
|
27 |
|
28 |
|
29 | function consoleTable(table) {
|
30 | console.table(table);
|
31 | }
|
32 |
|
33 | function warnInProduction() {
|
34 | if (alreadyWarned) {
|
35 | return;
|
36 | }
|
37 | alreadyWarned = true;
|
38 | if (typeof console !== 'undefined') {
|
39 | console.error('ReactPerf is not supported in the production builds of React. ' + 'To collect measurements, please use the development build of React instead.');
|
40 | }
|
41 | }
|
42 |
|
43 | function getLastMeasurements() {
|
44 | if (!(process.env.NODE_ENV !== 'production')) {
|
45 | warnInProduction();
|
46 | return [];
|
47 | }
|
48 |
|
49 | return ReactDebugTool.getFlushHistory();
|
50 | }
|
51 |
|
52 | function getExclusive() {
|
53 | var flushHistory = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getLastMeasurements();
|
54 |
|
55 | if (!(process.env.NODE_ENV !== 'production')) {
|
56 | warnInProduction();
|
57 | return [];
|
58 | }
|
59 |
|
60 | var aggregatedStats = {};
|
61 | var affectedIDs = {};
|
62 |
|
63 | function updateAggregatedStats(treeSnapshot, instanceID, timerType, applyUpdate) {
|
64 | var displayName = treeSnapshot[instanceID].displayName;
|
65 |
|
66 | var key = displayName;
|
67 | var stats = aggregatedStats[key];
|
68 | if (!stats) {
|
69 | affectedIDs[key] = {};
|
70 | stats = aggregatedStats[key] = {
|
71 | key: key,
|
72 | instanceCount: 0,
|
73 | counts: {},
|
74 | durations: {},
|
75 | totalDuration: 0
|
76 | };
|
77 | }
|
78 | if (!stats.durations[timerType]) {
|
79 | stats.durations[timerType] = 0;
|
80 | }
|
81 | if (!stats.counts[timerType]) {
|
82 | stats.counts[timerType] = 0;
|
83 | }
|
84 | affectedIDs[key][instanceID] = true;
|
85 | applyUpdate(stats);
|
86 | }
|
87 |
|
88 | flushHistory.forEach(function (flush) {
|
89 | var measurements = flush.measurements,
|
90 | treeSnapshot = flush.treeSnapshot;
|
91 |
|
92 | measurements.forEach(function (measurement) {
|
93 | var duration = measurement.duration,
|
94 | instanceID = measurement.instanceID,
|
95 | timerType = measurement.timerType;
|
96 |
|
97 | updateAggregatedStats(treeSnapshot, instanceID, timerType, function (stats) {
|
98 | stats.totalDuration += duration;
|
99 | stats.durations[timerType] += duration;
|
100 | stats.counts[timerType]++;
|
101 | });
|
102 | });
|
103 | });
|
104 |
|
105 | return Object.keys(aggregatedStats).map(function (key) {
|
106 | return _extends({}, aggregatedStats[key], {
|
107 | instanceCount: Object.keys(affectedIDs[key]).length
|
108 | });
|
109 | }).sort(function (a, b) {
|
110 | return b.totalDuration - a.totalDuration;
|
111 | });
|
112 | }
|
113 |
|
114 | function getInclusive() {
|
115 | var flushHistory = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getLastMeasurements();
|
116 |
|
117 | if (!(process.env.NODE_ENV !== 'production')) {
|
118 | warnInProduction();
|
119 | return [];
|
120 | }
|
121 |
|
122 | var aggregatedStats = {};
|
123 | var affectedIDs = {};
|
124 |
|
125 | function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
|
126 | var _treeSnapshot$instanc = treeSnapshot[instanceID],
|
127 | displayName = _treeSnapshot$instanc.displayName,
|
128 | ownerID = _treeSnapshot$instanc.ownerID;
|
129 |
|
130 | var owner = treeSnapshot[ownerID];
|
131 | var key = (owner ? owner.displayName + ' > ' : '') + displayName;
|
132 | var stats = aggregatedStats[key];
|
133 | if (!stats) {
|
134 | affectedIDs[key] = {};
|
135 | stats = aggregatedStats[key] = {
|
136 | key: key,
|
137 | instanceCount: 0,
|
138 | inclusiveRenderDuration: 0,
|
139 | renderCount: 0
|
140 | };
|
141 | }
|
142 | affectedIDs[key][instanceID] = true;
|
143 | applyUpdate(stats);
|
144 | }
|
145 |
|
146 | var isCompositeByID = {};
|
147 | flushHistory.forEach(function (flush) {
|
148 | var measurements = flush.measurements;
|
149 |
|
150 | measurements.forEach(function (measurement) {
|
151 | var instanceID = measurement.instanceID,
|
152 | timerType = measurement.timerType;
|
153 |
|
154 | if (timerType !== 'render') {
|
155 | return;
|
156 | }
|
157 | isCompositeByID[instanceID] = true;
|
158 | });
|
159 | });
|
160 |
|
161 | flushHistory.forEach(function (flush) {
|
162 | var measurements = flush.measurements,
|
163 | treeSnapshot = flush.treeSnapshot;
|
164 |
|
165 | measurements.forEach(function (measurement) {
|
166 | var duration = measurement.duration,
|
167 | instanceID = measurement.instanceID,
|
168 | timerType = measurement.timerType;
|
169 |
|
170 | if (timerType !== 'render') {
|
171 | return;
|
172 | }
|
173 | updateAggregatedStats(treeSnapshot, instanceID, function (stats) {
|
174 | stats.renderCount++;
|
175 | });
|
176 | var nextParentID = instanceID;
|
177 | while (nextParentID) {
|
178 |
|
179 |
|
180 | if (isCompositeByID[nextParentID]) {
|
181 | updateAggregatedStats(treeSnapshot, nextParentID, function (stats) {
|
182 | stats.inclusiveRenderDuration += duration;
|
183 | });
|
184 | }
|
185 | nextParentID = treeSnapshot[nextParentID].parentID;
|
186 | }
|
187 | });
|
188 | });
|
189 |
|
190 | return Object.keys(aggregatedStats).map(function (key) {
|
191 | return _extends({}, aggregatedStats[key], {
|
192 | instanceCount: Object.keys(affectedIDs[key]).length
|
193 | });
|
194 | }).sort(function (a, b) {
|
195 | return b.inclusiveRenderDuration - a.inclusiveRenderDuration;
|
196 | });
|
197 | }
|
198 |
|
199 | function getWasted() {
|
200 | var flushHistory = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getLastMeasurements();
|
201 |
|
202 | if (!(process.env.NODE_ENV !== 'production')) {
|
203 | warnInProduction();
|
204 | return [];
|
205 | }
|
206 |
|
207 | var aggregatedStats = {};
|
208 | var affectedIDs = {};
|
209 |
|
210 | function updateAggregatedStats(treeSnapshot, instanceID, applyUpdate) {
|
211 | var _treeSnapshot$instanc2 = treeSnapshot[instanceID],
|
212 | displayName = _treeSnapshot$instanc2.displayName,
|
213 | ownerID = _treeSnapshot$instanc2.ownerID;
|
214 |
|
215 | var owner = treeSnapshot[ownerID];
|
216 | var key = (owner ? owner.displayName + ' > ' : '') + displayName;
|
217 | var stats = aggregatedStats[key];
|
218 | if (!stats) {
|
219 | affectedIDs[key] = {};
|
220 | stats = aggregatedStats[key] = {
|
221 | key: key,
|
222 | instanceCount: 0,
|
223 | inclusiveRenderDuration: 0,
|
224 | renderCount: 0
|
225 | };
|
226 | }
|
227 | affectedIDs[key][instanceID] = true;
|
228 | applyUpdate(stats);
|
229 | }
|
230 |
|
231 | flushHistory.forEach(function (flush) {
|
232 | var measurements = flush.measurements,
|
233 | treeSnapshot = flush.treeSnapshot,
|
234 | operations = flush.operations;
|
235 |
|
236 | var isDefinitelyNotWastedByID = {};
|
237 |
|
238 |
|
239 |
|
240 | operations.forEach(function (operation) {
|
241 | var instanceID = operation.instanceID;
|
242 |
|
243 | var nextParentID = instanceID;
|
244 | while (nextParentID) {
|
245 | isDefinitelyNotWastedByID[nextParentID] = true;
|
246 | nextParentID = treeSnapshot[nextParentID].parentID;
|
247 | }
|
248 | });
|
249 |
|
250 |
|
251 |
|
252 | var renderedCompositeIDs = {};
|
253 | measurements.forEach(function (measurement) {
|
254 | var instanceID = measurement.instanceID,
|
255 | timerType = measurement.timerType;
|
256 |
|
257 | if (timerType !== 'render') {
|
258 | return;
|
259 | }
|
260 | renderedCompositeIDs[instanceID] = true;
|
261 | });
|
262 |
|
263 | measurements.forEach(function (measurement) {
|
264 | var duration = measurement.duration,
|
265 | instanceID = measurement.instanceID,
|
266 | timerType = measurement.timerType;
|
267 |
|
268 | if (timerType !== 'render') {
|
269 | return;
|
270 | }
|
271 |
|
272 |
|
273 |
|
274 | var updateCount = treeSnapshot[instanceID].updateCount;
|
275 |
|
276 | if (isDefinitelyNotWastedByID[instanceID] || updateCount === 0) {
|
277 | return;
|
278 | }
|
279 |
|
280 |
|
281 | updateAggregatedStats(treeSnapshot, instanceID, function (stats) {
|
282 | stats.renderCount++;
|
283 | });
|
284 |
|
285 | var nextParentID = instanceID;
|
286 | while (nextParentID) {
|
287 |
|
288 |
|
289 | var isWasted = renderedCompositeIDs[nextParentID] && !isDefinitelyNotWastedByID[nextParentID];
|
290 | if (isWasted) {
|
291 | updateAggregatedStats(treeSnapshot, nextParentID, function (stats) {
|
292 | stats.inclusiveRenderDuration += duration;
|
293 | });
|
294 | }
|
295 | nextParentID = treeSnapshot[nextParentID].parentID;
|
296 | }
|
297 | });
|
298 | });
|
299 |
|
300 | return Object.keys(aggregatedStats).map(function (key) {
|
301 | return _extends({}, aggregatedStats[key], {
|
302 | instanceCount: Object.keys(affectedIDs[key]).length
|
303 | });
|
304 | }).sort(function (a, b) {
|
305 | return b.inclusiveRenderDuration - a.inclusiveRenderDuration;
|
306 | });
|
307 | }
|
308 |
|
309 | function getOperations() {
|
310 | var flushHistory = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getLastMeasurements();
|
311 |
|
312 | if (!(process.env.NODE_ENV !== 'production')) {
|
313 | warnInProduction();
|
314 | return [];
|
315 | }
|
316 |
|
317 | var stats = [];
|
318 | flushHistory.forEach(function (flush, flushIndex) {
|
319 | var operations = flush.operations,
|
320 | treeSnapshot = flush.treeSnapshot;
|
321 |
|
322 | operations.forEach(function (operation) {
|
323 | var instanceID = operation.instanceID,
|
324 | type = operation.type,
|
325 | payload = operation.payload;
|
326 | var _treeSnapshot$instanc3 = treeSnapshot[instanceID],
|
327 | displayName = _treeSnapshot$instanc3.displayName,
|
328 | ownerID = _treeSnapshot$instanc3.ownerID;
|
329 |
|
330 | var owner = treeSnapshot[ownerID];
|
331 | var key = (owner ? owner.displayName + ' > ' : '') + displayName;
|
332 |
|
333 | stats.push({
|
334 | flushIndex: flushIndex,
|
335 | instanceID: instanceID,
|
336 | key: key,
|
337 | type: type,
|
338 | ownerID: ownerID,
|
339 | payload: payload
|
340 | });
|
341 | });
|
342 | });
|
343 | return stats;
|
344 | }
|
345 |
|
346 | function printExclusive(flushHistory) {
|
347 | if (!(process.env.NODE_ENV !== 'production')) {
|
348 | warnInProduction();
|
349 | return;
|
350 | }
|
351 |
|
352 | var stats = getExclusive(flushHistory);
|
353 | var table = stats.map(function (item) {
|
354 | var key = item.key,
|
355 | instanceCount = item.instanceCount,
|
356 | totalDuration = item.totalDuration;
|
357 |
|
358 | var renderCount = item.counts.render || 0;
|
359 | var renderDuration = item.durations.render || 0;
|
360 | return {
|
361 | Component: key,
|
362 | 'Total time (ms)': roundFloat(totalDuration),
|
363 | 'Instance count': instanceCount,
|
364 | 'Total render time (ms)': roundFloat(renderDuration),
|
365 | 'Average render time (ms)': renderCount ? roundFloat(renderDuration / renderCount) : undefined,
|
366 | 'Render count': renderCount,
|
367 | 'Total lifecycle time (ms)': roundFloat(totalDuration - renderDuration)
|
368 | };
|
369 | });
|
370 | consoleTable(table);
|
371 | }
|
372 |
|
373 | function printInclusive(flushHistory) {
|
374 | if (!(process.env.NODE_ENV !== 'production')) {
|
375 | warnInProduction();
|
376 | return;
|
377 | }
|
378 |
|
379 | var stats = getInclusive(flushHistory);
|
380 | var table = stats.map(function (item) {
|
381 | var key = item.key,
|
382 | instanceCount = item.instanceCount,
|
383 | inclusiveRenderDuration = item.inclusiveRenderDuration,
|
384 | renderCount = item.renderCount;
|
385 |
|
386 | return {
|
387 | 'Owner > Component': key,
|
388 | 'Inclusive render time (ms)': roundFloat(inclusiveRenderDuration),
|
389 | 'Instance count': instanceCount,
|
390 | 'Render count': renderCount
|
391 | };
|
392 | });
|
393 | consoleTable(table);
|
394 | }
|
395 |
|
396 | function printWasted(flushHistory) {
|
397 | if (!(process.env.NODE_ENV !== 'production')) {
|
398 | warnInProduction();
|
399 | return;
|
400 | }
|
401 |
|
402 | var stats = getWasted(flushHistory);
|
403 | var table = stats.map(function (item) {
|
404 | var key = item.key,
|
405 | instanceCount = item.instanceCount,
|
406 | inclusiveRenderDuration = item.inclusiveRenderDuration,
|
407 | renderCount = item.renderCount;
|
408 |
|
409 | return {
|
410 | 'Owner > Component': key,
|
411 | 'Inclusive wasted time (ms)': roundFloat(inclusiveRenderDuration),
|
412 | 'Instance count': instanceCount,
|
413 | 'Render count': renderCount
|
414 | };
|
415 | });
|
416 | consoleTable(table);
|
417 | }
|
418 |
|
419 | function printOperations(flushHistory) {
|
420 | if (!(process.env.NODE_ENV !== 'production')) {
|
421 | warnInProduction();
|
422 | return;
|
423 | }
|
424 |
|
425 | var stats = getOperations(flushHistory);
|
426 | var table = stats.map(function (stat) {
|
427 | return {
|
428 | 'Owner > Node': stat.key,
|
429 | Operation: stat.type,
|
430 | Payload: typeof stat.payload === 'object' ? JSON.stringify(stat.payload) : stat.payload,
|
431 | 'Flush index': stat.flushIndex,
|
432 | 'Owner Component ID': stat.ownerID,
|
433 | 'DOM Component ID': stat.instanceID
|
434 | };
|
435 | });
|
436 | consoleTable(table);
|
437 | }
|
438 |
|
439 | var warnedAboutPrintDOM = false;
|
440 | function printDOM(measurements) {
|
441 | lowPriorityWarning(warnedAboutPrintDOM, '`ReactPerf.printDOM(...)` is deprecated. Use ' + '`ReactPerf.printOperations(...)` instead.');
|
442 | warnedAboutPrintDOM = true;
|
443 | return printOperations(measurements);
|
444 | }
|
445 |
|
446 | var warnedAboutGetMeasurementsSummaryMap = false;
|
447 | function getMeasurementsSummaryMap(measurements) {
|
448 | lowPriorityWarning(warnedAboutGetMeasurementsSummaryMap, '`ReactPerf.getMeasurementsSummaryMap(...)` is deprecated. Use ' + '`ReactPerf.getWasted(...)` instead.');
|
449 | warnedAboutGetMeasurementsSummaryMap = true;
|
450 | return getWasted(measurements);
|
451 | }
|
452 |
|
453 | function start() {
|
454 | if (!(process.env.NODE_ENV !== 'production')) {
|
455 | warnInProduction();
|
456 | return;
|
457 | }
|
458 |
|
459 | ReactDebugTool.beginProfiling();
|
460 | }
|
461 |
|
462 | function stop() {
|
463 | if (!(process.env.NODE_ENV !== 'production')) {
|
464 | warnInProduction();
|
465 | return;
|
466 | }
|
467 |
|
468 | ReactDebugTool.endProfiling();
|
469 | }
|
470 |
|
471 | function isRunning() {
|
472 | if (!(process.env.NODE_ENV !== 'production')) {
|
473 | warnInProduction();
|
474 | return false;
|
475 | }
|
476 |
|
477 | return ReactDebugTool.isProfiling();
|
478 | }
|
479 |
|
480 | var ReactPerfAnalysis = {
|
481 | getLastMeasurements: getLastMeasurements,
|
482 | getExclusive: getExclusive,
|
483 | getInclusive: getInclusive,
|
484 | getWasted: getWasted,
|
485 | getOperations: getOperations,
|
486 | printExclusive: printExclusive,
|
487 | printInclusive: printInclusive,
|
488 | printWasted: printWasted,
|
489 | printOperations: printOperations,
|
490 | start: start,
|
491 | stop: stop,
|
492 | isRunning: isRunning,
|
493 |
|
494 | printDOM: printDOM,
|
495 | getMeasurementsSummaryMap: getMeasurementsSummaryMap
|
496 | };
|
497 |
|
498 | module.exports = ReactPerfAnalysis; |
\ | No newline at end of file |