Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Les performances Web: Soyez responsable!

Les performances Web: Soyez responsable!

Jamais les connections internet n’auront été aussi rapides. Pourtant la perception est que le web est de plus en plus lent. Un usager exige qu’une page web soit disponible le plus vite possible, souvent sous les 2 secondes. Les 100 sites les plus consultés au monde ne sont même pas proche de cette limite psychologique.

Il n’y a rien de plus frustrant que d’attendre. Il devient donc impératif d’avoir un site web qui s’affiche rapidement, peu importe le type d’appareil utilisé. Surtout dernièrement, la navigation mobile a surpassé celle de l’ordinateur de bureau. La vitesse de chargement devient donc une fonctionnalité importante pour accrocher et conserver les usagers qui fréquentent votre site.

Durant cette conférence, nous démystifierons l’optimisation du temps de chargement d’une page web à l’aide de quelques techniques et outils, que vous pourrez facilement intégrer à votre méthodologie de développement.

71bc55f8402e72d15a57cd37bf68ae89?s=128

Pierre-Luc Babin

March 18, 2015
Tweet

More Decks by Pierre-Luc Babin

Other Decks in Programming

Transcript

  1. Performances web Soyez responsable!

  2. @plbabin

  3. None
  4. 2.0MB

  5. 1.3MB Images 307KB JavaScript 63KB css 60KB html

  6. None
  7. Come on man!

  8. Brad Frost « Good performance is good design »

  9. la rapidité n’est pas qu’une fonctionnalité, c’est une obligation

  10. vitesse == bonheur

  11. temps de chargement 2 secondes

  12. >3 secondes, 40% vont quitter votre site Source: Etsy

  13. la rapidité est la 2e fonctionnalité la plus importante Source:

    The Guardian
  14. 80% frontend

  15. comment?

  16. les performances doivent devenir une étape importante dans votre quotidien

  17. tout le monde doit s’impliquer

  18. job de coaching

  19. évitez d’avoir un spécialiste

  20. plus que 2 semaines par année

  21. mesurer. optimiser. recommencer.

  22. mesurer. optimiser. recommencer.

  23. métriques

  24. speed index

  25. temps (ms) avant que le premier pixel soit affiché à

    l’écran
  26. Perception de vitesse

  27. ~1500ms

  28. faire un budget

  29. Un budget de performance est une limite imposée sur certaines

    métriques, pour chaque page de votre site.
  30. max 600KB de téléchargement max 40 requêtes speed index de

    ~1500
  31. http://www.webpagetest.org/

  32. http://www.webpagetest.org/

  33. https://developers.google.com/speed/pagespeed/insights/

  34. http://speedcurve.com/

  35. None
  36. https://github.com/tkadlec/grunt-perfbudget grunt-perfbudget

  37. https://github.com/tkadlec/grunt-perfbudget perfbudget: { default: { options: { url: ‘http://pinkman-biatch.com', key:

    'WEBPAGETEST_API_KEY', runs: 5, budget: { requests: '40', SpeedIndex: ‘1000’, bytesIn: ‘400’ } } } }
  38. mesurer. optimiser. recommencer.

  39. BASE

  40. requêtes HTTP

  41. Steve Souders « La meilleure requête est celle qu’on ne

    fait pas » Requêtes HTTP
  42. Requêtes HTTP 139ms 35ms 1280ms 2141ms

  43. Requêtes HTTP DNS lookup HTTP Request TCP Connection Content download

    139ms 35ms 1280ms 2141ms
  44. Requêtes HTTP

  45. Chapter 10. Primer on Web Performance

  46. Chapter 10. Primer on Web Performance requêtes plus petites moins

    de requêtes
  47. la latence est ce qui ralenti le plus nos site

    web
  48. position dans le html

  49. CSS dans le head <!DOCTYPE html> <html> <head> <link href="yeah/bitch.12947498.css"

    rel="stylesheet"> <title>Jesse Pinkman Official Fan Page</title> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /> </head> <body> ... </body> </html>
  50. <!DOCTYPE html> <html> <head> <link href="yeah/bitch.12947498.css" rel="stylesheet"> <title>Jesse Pinkman Official

    Fan Page</title> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /> </head> <body> ... </body> </html> CSS dans le head
  51. None
  52. Javascript à la fin du body <!DOCTYPE html> <html> <head>

    <link href="yeah/bitch.12947498.css" rel="stylesheet"> <title>Jesse Pinkman Official Fan Page</title> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /> </head> <body> ... <script type='text/javascript' src='yeah.bitch.magnets!.13875.js'></script> </body> </html>
  53. Javascript à la fin du body <!DOCTYPE html> <html> <head>

    <link href="yeah/bitch.12947498.css" rel="stylesheet"> <title>Jesse Pinkman Official Fan Page</title> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="viewport" content="width=device-width,initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /> </head> <body> ... <script type='text/javascript' src='yeah.bitch.magnets!.13875.js'></script> </body> </html>
  54. @import

  55. fusion + minification

  56. @charset "UTF-8";.text-center{text-align:center}.text-left{text- align:left}.text-right{text-align:right}.pull- left{float:left}.pull- right{float:right}.clearfix:before,.clearfix:after{display:table; content:" "}.clearfix:after{clear:both}.hidden{display:none}.header- controls .header-controls__dropdown a:after,.media-object--

    folder:before,.media-object--usb:before,.menubarIcon-- volume:before,.menubarIcon--volumeHigh:before,.menubarIcon-- volumeLow:before,.media-object--photo:before,.icon-- refresh:before,.menubarIcon--search:before,.sp- search__form:before,li.header- controls__search:after,.menubarIcon--settings:before,.icon-- fav:before,.media-object--video:before,.menubarIcon-- volumeNone:before,.menubarIcon--wifi:before,.menubarIcon-- wifi__networks:after,.icon--compose:before,.musicControl-- next:before,.media-object--file:before,.header-controls__button-- grid:before,.menubarIcon--battery:before,.musicControl-- pause:before,.header-controls__button-- share:before,.widget__messages:before,.musicControl-- prev:before,.header-controls__button-- list:before,.calendar__nav--left:before,.calendar__nav-- right:before{font-style:normal;font-weight:400;text- decoration:none;text-rendering:optimizeLegibility;white- space:nowrap;-ms-font-feature-settings:"liga" 1;-webkit-font- feature-settings:"liga";font-feature-settings:"liga";-webkit- font-smoothing:antialiased}html:hover .header-controls .header- controls__dropdown a:after,.header-controls .header- controls__dropdown html:hover a:after,html:hover .media-object-- folder:before,html:hover .media-object-- usb:before,html:hover .menubarIcon-- volume:before,html:hover .menubarIcon-- volumeHigh:before,html:hover .menubarIcon-- volumeLow:before,html:hover .media-object-- photo:before,html:hover .icon-- refresh:before,html:hover .menubarIcon-- search:before,html:hover .sp-search__form:before,html:hover li.header-controls__search:after,html:hover .menubarIcon-- settings:before,html:hover .icon--fav:before,html:hover .media- object--video:before,html:hover .menubarIcon-- volumeNone:before,html:hover .menubarIcon--wifi:before,html:hover .menubarIcon--wifi__networks:after,html:hover .icon-- compose:before,html:hover .musicControl-- next:before,html:hover .media-object-- file:before,html:hover .header-controls__button-- grid:before,html:hover .menubarIcon-- minification // Icon Listings // -------------------------------------------------- .applications__listing { margin: 0 -10px; // account for icon margins padding: 0; } .applications__icon { // Width and height need padding added around. width: ($application-icon + 20); position: relative; display: block; margin: 10px 10px 0; float: left; text-transform: uppercase; text-align: center; } .applications__icon a { display: block; line-height: 20px; font-size: 11px; color: #fff; padding-top:$application-icon + 20px; position: relative; &:before{ background-color: rgba($action-active, 0); height: ($application-icon + 20); position:absolute; content:''; width:100%; top:0; left:0; border-radius: 3px; } &:hover:before { transition: .075s all linear; background-color: rgba($action-active, .5); } }
  57. https://github.com/gruntjs/grunt-contrib-cssmin grunt-contrib-cssmin

  58. https://github.com/gruntjs/grunt-contrib-uglify grunt-contrib-uglify

  59. images

  60. compression

  61. https://imageoptim.com/fr.html

  62. https://github.com/JamieMason/grunt-imageoptim grunt-imageoptim

  63. utiliser la bonne dimension (pixel)

  64. sprites

  65. https://github.com/Ensighten/grunt-spritesmith grunt-spritesmith

  66. grunt-spritesmith en action .icon-sylvain_carle-227x190-150x150 { background-image: url(sprite_avatar.jpg); background-position: -420px -126px;

    width: 42px; height: 42px; } .icon-waq-david-eaves-150x150 { background-image: url(sprite_avatar.jpg); background-position: -42px -252px; width: 42px; height: 42px; } .icon-waq-hilary-mason-150x150 { background-image: url(sprite_avatar.jpg); background-position: -84px -252px; width: 42px; height: 42px; } .icon-waq-jean-marc-dejonghe-150x150 { background-image: url(sprite_avatar.jpg); background-position: -252px -84px; width: 42px; height: 42px; }
  67. data-uri

  68. data-uri li { background: url(data:image/ gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvC chPGolfO0o/XBs/fNwfjZ0frl3/zy7//// wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqN KToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAVi

    FIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7) no-repeat left center; padding: 5px 0 5px 25px; }
  69. GZip

  70. Sans GZip Avec Gzip index.php 230kb 28.9kb application.css 179kb 24.2kb

    scripts.js 147kb 48.5kb
  71. http://gzipwtf.com/

  72. Cache HTTP

  73. configuration nginx # cache.appcache, your document html and data location

    ~* \.(?:manifest|appcache|html?|xml|json)$ { expires -1; access_log logs/static.log; } # Feed location ~* \.(?:rss|atom)$ { expires 1h; add_header Cache-Control "public"; } # Media: images, icons, video, audio, HTC location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { expires 1M; access_log off; add_header Cache-Control "public"; } # CSS and Javascript location ~* \.(?:css|js)$ { expires 1y; access_log off; add_header Cache-Control "public"; }
  74. configuration apache # CSS ExpiresByType text/css "access plus 1 year"

    # Data interchange ExpiresByType application/json "access plus 0 seconds" ExpiresByType application/ld+json "access plus 0 seconds" ExpiresByType application/schema+json "access plus 0 seconds" ExpiresByType application/vnd.geo+json "access plus 0 seconds" ExpiresByType application/xml "access plus 0 seconds" ExpiresByType text/xml "access plus 0 seconds" # Favicon (cannot be renamed!) and cursor images ExpiresByType image/vnd.microsoft.icon "access plus 1 week" ExpiresByType image/x-icon "access plus 1 week" # JavaScript ExpiresByType application/javascript "access plus 1 year" ExpiresByType application/x-javascript "access plus 1 year" ExpiresByType text/javascript "access plus 1 year" # Media files ExpiresByType audio/ogg "access plus 1 month" ExpiresByType image/bmp "access plus 1 month" ExpiresByType image/gif "access plus 1 month" ExpiresByType image/jpeg "access plus 1 month" ExpiresByType image/png "access plus 1 month" ExpiresByType image/svg+xml "access plus 1 month" ExpiresByType image/webp "access plus 1 month" ExpiresByType video/mp4 "access plus 1 month" ExpiresByType video/ogg "access plus 1 month" ExpiresByType video/webm "access plus 1 month"
  75. Zone Expert

  76. CSS Critique

  77. 14kb

  78. 1. Déterminer le contenu prioritaire 2. Créer le CSS 3.

    Injecter le CSS/Javascript
  79. 1. Déterminer le contenu prioritaire 2. Créer le CSS 3.

    Injecter le CSS/Javascript
  80. Déterminer le contenu prioritaire

  81. Déterminer le contenu prioritaire

  82. 1. Déterminer le contenu prioritaire 2. Créer le CSS 3.

    Injecter le CSS/Javascript
  83. Créer le css critique // ==================================== // IMPORTS - CRITICAL

    CSS // ==================================== @import "reset.css" @import "partials/mixins" @import "partials/variables" @import "partials/base" @import "partials/typography" @import "partials/icons" @import "partials/layout" @import "partials/navigation"
  84. https://github.com/filamentgroup/grunt-criticalcss grunt-criticalcss

  85. 1. Déterminer le contenu prioritaire 2. Créer le CSS 3.

    Injecter le CSS/Javascript
  86. https://github.com/filamentgroup/loadCSS loadCSS

  87. Injecter le CSS critique <!DOCTYPE html> <html> <head> <meta name="css-all"

    content="yeah/bitch.12947498.css"> <style> /* CSS Critique */ button,html,input,select,textarea{color:#000;} ...; </style> <script> // inline load.css (function( window, undefined ) { function getMeta( metaname ){ ... }; function cookie( name, value, days ){ ... } function loadCSS( href, before, media, callback ){ ... } var cssfile = getMeta('css-all'); if( cssfile && !cookie( cssfile ) ){ loadCSS( cssfile ); cookie( cssfile, "true", 7 ); // pour la cache côté serveur } }( this )); </script> <noscript> <link href="yeah/bitch.12947498.css" rel="stylesheet"> </noscript> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... </body> </html>
  88. CSS critique dans le head <!DOCTYPE html> <html> <head> <meta

    name="css-all" content="yeah/bitch.12947498.css"> <style> /* CSS Critique */ button,html,input,select,textarea{color:#000;} ...; </style> <script> // inline load.css (function( window, undefined ) { function getMeta( metaname ){ ... }; function cookie( name, value, days ){ ... } function loadCSS( href, before, media, callback ){ ... } var cssfile = getMeta('css-all'); if( cssfile && !cookie( cssfile ) ){ loadCSS( cssfile ); cookie( cssfile, "true", 7 ); // pour la cache côté serveur } }( this )); </script> <noscript> <link href="yeah/bitch.12947498.css" rel="stylesheet"> </noscript> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... </body> </html>
  89. Meta dans le head <!DOCTYPE html> <html> <head> <meta name="css-all"

    content="yeah/bitch.12947498.css"> <style> /* CSS Critique */ button,html,input,select,textarea{color:#000;} ...; </style> <script> // inline load.css (function( window, undefined ) { function getMeta( metaname ){ ... }; function cookie( name, value, days ){ ... } function loadCSS( href, before, media, callback ){ ... } var cssfile = getMeta('css-all'); if( cssfile && !cookie( cssfile ) ){ loadCSS( cssfile ); cookie( cssfile, "true", 7 ); // pour la cache côté serveur } }( this )); </script> <noscript> <link href="yeah/bitch.12947498.css" rel="stylesheet"> </noscript> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... </body> </html>
  90. Fonctions pour télécharger le css <!DOCTYPE html> <html> <head> <meta

    name="css-all" content="yeah/bitch.12947498.css"> <style> /* CSS Critique */ button,html,input,select,textarea{color:#000;} ...; </style> <script> // inline load.css (function( window, undefined ) { function getMeta( metaname ){ ... }; function cookie( name, value, days ){ ... } function loadCSS( href, before, media, callback ){ ... } var cssfile = getMeta('css-all'); if( cssfile && !cookie( cssfile ) ){ loadCSS( cssfile ); cookie( cssfile, "true", 7 ); // pour la cache côté serveur } }( this )); </script> <noscript> <link href="yeah/bitch.12947498.css" rel="stylesheet"> </noscript> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... </body> </html>
  91. Télécharge le CSS principal <!DOCTYPE html> <html> <head> <meta name="css-all"

    content="yeah/bitch.12947498.css"> <style> /* CSS Critique */ button,html,input,select,textarea{color:#000;} ...; </style> <script> // inline load.css (function( window, undefined ) { function getMeta( metaname ){ ... }; function cookie( name, value, days ){ ... } function loadCSS( href, before, media, callback ){ ... } var cssfile = getMeta('css-all'); if( cssfile && !cookie( cssfile ) ){ loadCSS( cssfile ); cookie( cssfile, "true", 7 ); // pour la cache côté serveur } }( this )); </script> <noscript> <link href="yeah/bitch.12947498.css" rel="stylesheet"> </noscript> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... </body> </html>
  92. Noscript <!DOCTYPE html> <html> <head> <meta name="css-all" content="yeah/bitch.12947498.css"> <style> /*

    CSS Critique */ button,html,input,select,textarea{color:#000;} ...; </style> <script> // inline load.css (function( window, undefined ) { function getMeta( metaname ){ ... }; function cookie( name, value, days ){ ... } function loadCSS( href, before, media, callback ){ ... } var cssfile = getMeta('css-all'); if( cssfile && !cookie( cssfile ) ){ loadCSS( cssfile ); cookie( cssfile, "true", 7 ); // pour la cache côté serveur } }( this )); </script> <noscript> <link href="yeah/bitch.12947498.css" rel="stylesheet"> </noscript> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... </body> </html>
  93. Cache <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="yeah/bitch.12947498.css"> <title>Jesse Pinkman

    Official Fan Page</title> </head> <body> ... </body> </html>
  94. Cache <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="yeah/bitch.12947498.css"> <title>Jesse Pinkman

    Official Fan Page</title> </head> <body> ... </body> </html>
  95. @font-face

  96. Optimiser l’injection de @font-face

  97. Font Load Events

  98. http://caniuse.com/#search=font%20loading

  99. https://github.com/bramstein/fontfaceobserver FontFaceObserver

  100. Optimiser l’injection de @font-face @font-face { font-family: 'Proxima Nova'; src:

    url('/css/fonts/proximanova-light-webfont.woff2') format('woff2'), url('/css/fonts/proximanova-light-webfont.woff') format('woff'), url('/css/fonts/proximanova-light-webfont.ttf') format('truetype'); font-weight: 300; font-style: normal; } body { font-family: Arial, sans-serif; } .proxima-loaded body { font-family: Proxima Nova, Arial, sans-serif; }
  101. Optimiser l’injection de @font-face @font-face { font-family: 'Proxima Nova'; src:

    url('/css/fonts/proximanova-light-webfont.woff2') format('woff2'), url('/css/fonts/proximanova-light-webfont.woff') format('woff'), url('/css/fonts/proximanova-light-webfont.ttf') format('truetype'); font-weight: 300; font-style: normal; } body { font-family: Arial, sans-serif; } .proxima-loaded body { font-family: Proxima Nova, Arial, sans-serif; }
  102. Optimiser l’injection de @font-face @font-face { font-family: 'Proxima Nova'; src:

    url('/css/fonts/proximanova-light-webfont.woff2') format('woff2'), url('/css/fonts/proximanova-light-webfont.woff') format('woff'), url('/css/fonts/proximanova-light-webfont.ttf') format('truetype'); font-weight: 300; font-style: normal; } body { font-family: Arial, sans-serif; } .proxima-loaded body { font-family: Proxima Nova, Arial, sans-serif; }
  103. Optimiser l’injection de @font-face @font-face { font-family: 'Proxima Nova'; src:

    url('/css/fonts/proximanova-light-webfont.woff2') format('woff2'), url('/css/fonts/proximanova-light-webfont.woff') format('woff'), url('/css/fonts/proximanova-light-webfont.ttf') format('truetype'); font-weight: 300; font-style: normal; } body { font-family: Arial, sans-serif; } .proxima-loaded body { font-family: Proxima Nova, Arial, sans-serif; }
  104. Optimiser l’injection de @font-face <!DOCTYPE html> <html> <head> <link rel="stylesheet"

    href="yeah/bitch.12947498.css"> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... <script> /* inline le polyfil https://github.com/bramstein/fontfaceobserver/ */ (function( w ){ if( w.document.documentElement.className.indexOf( "proxima-loaded" ) > -1 ) return; var fontA = new w.FontFaceObserver( "Proxima Nova", { weight: 300 }); var fontB = new w.FontFaceObserver( "Proxima Nova", { weight: 600 }); w.Promise.all( [fontA.check(), fontB.check()] ).then(function(){ w.document.documentElement.className += " proxima-loaded"; cookie( "proxima-loaded", "true", 7 ); }); }( this )); </script> </body> </html>
  105. Optimiser l’injection de @font-face <!DOCTYPE html> <html> <head> <link rel="stylesheet"

    href="yeah/bitch.12947498.css"> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... <script> /* inline le polyfil https://github.com/bramstein/fontfaceobserver/ */ (function( w ){ if( w.document.documentElement.className.indexOf( "proxima-loaded" ) > -1 ) return; var fontA = new w.FontFaceObserver( "Proxima Nova", { weight: 300 }); var fontB = new w.FontFaceObserver( "Proxima Nova", { weight: 600 }); w.Promise.all( [fontA.check(), fontB.check()] ).then(function(){ w.document.documentElement.className += " proxima-loaded"; cookie( "proxima-loaded", "true", 7 ); }); }( this )); </script> </body> </html>
  106. Optimiser l’injection de @font-face <!DOCTYPE html> <html> <head> <link rel="stylesheet"

    href="yeah/bitch.12947498.css"> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... <script> /* inline le polyfil https://github.com/bramstein/fontfaceobserver/ */ (function( w ){ if( w.document.documentElement.className.indexOf( "proxima-loaded" ) > -1 ) return; var fontA = new w.FontFaceObserver( "Proxima Nova", { weight: 300 }); var fontB = new w.FontFaceObserver( "Proxima Nova", { weight: 600 }); w.Promise.all( [fontA.check(), fontB.check()] ).then(function(){ w.document.documentElement.className += " proxima-loaded"; cookie( "proxima-loaded", "true", 7 ); }); }( this )); </script> </body> </html>
  107. Optimiser l’injection de @font-face <!DOCTYPE html> <html class="proxima-loaded"> <head> <link

    rel="stylesheet" href="yeah/bitch.12947498.css"> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... </body> </html>
  108. Optimiser l’injection de @font-face <!DOCTYPE html> <html class="proxima-loaded"> <head> <link

    rel="stylesheet" href="yeah/bitch.12947498.css"> <title>Jesse Pinkman Official Fan Page</title> </head> <body> ... </body> </html>
  109. woff 2.0

  110. http://caniuse.com/#search=woff2

  111. http://caniuse.com/#search=woff

  112. 216k? 12 font-weight?

  113. CSS Post Processor

  114. https://github.com/nDmitry/grunt-postcss grunt-postcss

  115. https://github.com/JohnCashmore/grunt-cssshrink grunt-cssshrink

  116. https://github.com/addyosmani/grunt-uncss grunt-uncss

  117. Audits http://webaquebec.org

  118. mesurer. optimiser. recommencer.

  119. mod_pagespeed ngx_pagespeed HTTP 2/0 Picturefil 2

  120. None
  121. Tout le monde est responsable

  122. changer la culture envers les performances web

  123. Métriques pour se comparer

  124. Prendre conscience de l’importance d’un site rapide

  125. Merci!