windowPerfYLT.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /**
  2. * Measure when the page reaches certain states
  3. *
  4. * @see http://w3c-test.org/webperf/specs/NavigationTiming/#dom-performancetiming-domloading
  5. * @see https://developers.google.com/web/fundamentals/performance/critical-rendering-path/measure-crp
  6. */
  7. /* global document: true, window: true */
  8. exports.version = '1.0.a';
  9. exports.module = function(phantomas) {
  10. 'use strict';
  11. // times below are calculated relative to performance.timing.responseEnd (#117)
  12. phantomas.setMetric('domInteractive'); // @desc time it took to parse the HTML and construct the DOM
  13. phantomas.setMetric('domContentLoaded'); // @desc time it took to construct both DOM and CSSOM, no stylesheets that are blocking JavaScript execution (i.e. onDOMReady)
  14. phantomas.setMetric('domContentLoadedEnd'); // @desc time it took to finish handling of onDOMReady event @unreliable
  15. phantomas.setMetric('domComplete'); // @desc time it took to load all page resources, the loading spinner has stopped spinning
  16. // backend vs frontend time
  17. phantomas.setMetric('timeBackend'); // @desc time to the first byte compared to the total loading time [%]
  18. phantomas.setMetric('timeFrontend'); // @desc time to window.load compared to the total loading time [%]
  19. // measure dom... metrics from the moment HTML response was fully received
  20. var responseEndTime = Date.now();
  21. phantomas.on('responseEnd', function() {
  22. responseEndTime = Date.now();
  23. phantomas.log('Performance timing: responseEnd = %d', responseEndTime);
  24. });
  25. phantomas.on('init', function() {
  26. phantomas.evaluate(function(responseEndTime) {
  27. (function(phantomas) {
  28. phantomas.spyEnabled(false, 'installing window.performance metrics');
  29. phantomas.currentStep = 'domCreation';
  30. // extend window.performance
  31. // "init" event is sometimes fired twice, pass a value set by "responseEnd" event handler (fixes #192)
  32. if (typeof window.performance === 'undefined') {
  33. window.performance = {
  34. timing: {
  35. responseEnd: responseEndTime
  36. }
  37. };
  38. phantomas.log('Performance timing: emulating window.performance');
  39. }
  40. else {
  41. phantomas.log('Performance timing: using native window.performance');
  42. }
  43. // onDOMReady
  44. document.addEventListener("DOMContentLoaded", function() {
  45. setTimeout(function() {
  46. // use NavigationTiming if possible
  47. var time = window.performance.timing.domContentLoadedEventEnd ?
  48. (window.performance.timing.domContentLoadedEventEnd - window.performance.timing.responseEnd)
  49. :
  50. (Date.now() - responseEndTime);
  51. phantomas.currentStep = 'domContentLoadedEnd';
  52. phantomas.setMetric('domContentLoadedEnd', time, true);
  53. phantomas.log('Performance timing: document reached "DOMContentLoadedEnd" state after %d ms', time);
  54. phantomas.pushContext({
  55. type: 'domContentLoadedEnd'
  56. });
  57. }, 0);
  58. var time = Date.now() - responseEndTime;
  59. phantomas.currentStep = 'domContentLoaded';
  60. phantomas.setMetric('domContentLoaded', time, true);
  61. phantomas.log('Performance timing: document reached "DOMContentLoaded" state after %d ms', time);
  62. phantomas.pushContext({
  63. type: 'domContentLoaded'
  64. });
  65. });
  66. // emulate Navigation Timing
  67. document.addEventListener('readystatechange', function() {
  68. var readyState = document.readyState,
  69. responseEndTime = window.performance.timing.responseEnd,
  70. time = Date.now() - responseEndTime,
  71. metricName;
  72. // @see http://www.w3.org/TR/html5/dom.html#documentreadystate
  73. switch(readyState) {
  74. // the browser has finished parsing all of the HTML and DOM construction is complete
  75. case 'interactive':
  76. metricName = 'domInteractive';
  77. break;
  78. // the processing is complete and all of the resources on the page have finished downloading
  79. case 'complete':
  80. metricName = 'domComplete';
  81. phantomas.log('Performance timing: %j', window.performance.timing);
  82. break;
  83. default:
  84. phantomas.log('Performance timing: unhandled "%s" state!', readyState);
  85. return;
  86. }
  87. phantomas.currentStep = metricName;
  88. phantomas.setMetric(metricName, time, true);
  89. phantomas.log('Performance timing: document reached "%s" state after %d ms', readyState, time);
  90. phantomas.pushContext({
  91. type: metricName
  92. });
  93. });
  94. phantomas.spyEnabled(true);
  95. })(window.__phantomas);
  96. }, responseEndTime);
  97. });
  98. /**
  99. * Emit metrics with backend vs frontend time
  100. *
  101. * Performance Golden Rule:
  102. * "80-90% of the end-user response time is spent on the frontend. Start there."
  103. *
  104. * @see http://www.stevesouders.com/blog/2012/02/10/the-performance-golden-rule/
  105. */
  106. phantomas.on('report', function() {
  107. // The “backend” time is the time it takes the server to get the first byte back to the client.
  108. // The “frontend” time is measured from the last byte of the response (responseEnd) until all resources are fetched (domComplete)
  109. var backendTime = parseInt(phantomas.getMetric('timeToFirstByte'), 10),
  110. frontendTime = parseInt(phantomas.getMetric('domComplete'), 10),
  111. totalTime = backendTime + frontendTime,
  112. backendTimePercentage;
  113. if (totalTime === 0) {
  114. return;
  115. }
  116. backendTimePercentage = Math.round(backendTime / totalTime * 100);
  117. phantomas.setMetric('timeBackend', backendTimePercentage);
  118. phantomas.setMetric('timeFrontend', 100 - backendTimePercentage);
  119. phantomas.log('Performance timing: backend vs frontend time - %d% / %d%', backendTimePercentage, 100 - backendTimePercentage);
  120. });
  121. };