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