Website Performance

A presentation at JavaScript, Angular, React, HTML & CSS Days in München in March 2023 in Munich, Germany by Thomas Puppe

Slide 1

Slide 1

Trainer München Thomas Puppe 22. März 2023 Workshop @ JS Days München 2023 Website Performance

Slide 2

Slide 2

👋 Hi, ich bin Thomas Senior Frontend Developer bei ZEIT Online. #webperf #a11y #leanweb​

Slide 3

Slide 3

Website Performance

Slide 4

Slide 4

Slide 5

Slide 5

Credit: httparchive.org

Slide 6

Slide 6

Credits: https://de.statista.com/infografik/29079/durchschnittliche-kosten-fuer-1-gb-datenvolumen-in-ausgewaehlten-laendern-regionen/ , https://de.statista.com/infografik/24774/anteil-der-befragten-nach-der-groesse-des-inklusiven-datenvolumens/​

Slide 7

Slide 7

Credit (left to right): Anastasia Nelen auf Unsplash

Slide 8

Slide 8

Credit: Pawel Czerwinski auf Unsplash

Slide 9

Slide 9

“According to Google’s DoubleClick, when comparing sites that load in 5 seconds to sites that load in 19 seconds, the faster sites had 70% longer average session lengths, 35% lower bounce rates and 25% higher ad viewability than their slower counterparts.” – wpostats.com

Slide 10

Slide 10

“Rebuilding Pinterest pages for performance resulted in a 40% decrease in wait time, a 15% increase in SEO traffic and a 15% increase in conversion rate to signup.” – wpostats.com

Slide 11

Slide 11

“SpeedSense worked with an e-commerce company to improve performance and saw a 7.6% increase in sitewide conversion, translating to roughly a $6 million lift in annual revenue. Mobile transactions increased by nearly 30% and sales per session increased by 16%.” – wpostats.com

Slide 12

Slide 12

“Rakuten 24 ran an A/B test showing: improved vitals brought a 53.4% incrase in revenue per visitor, 33.1% increase in conversion rate, 15.2% increase in average order value, 35.1% reduction in exit rate and more!” – wpostats.com

Slide 13

Slide 13

Credit: wpostats.com

Slide 14

Slide 14

Credit: Markus Spiske auf Unsplash

Slide 15

Slide 15

Website 😻 Performance

Slide 16

Slide 16

Aspekte von Aus Usersicht Website schnell / schlank / ruckelt nicht Performance Browser / Technologien HTML / CSS / Bilder / JavaScript / Schriften / Video / Audio Build / Server Bildformate / Komprimierung / CDN, Cloud / Caching / Netzwerk Wirkung Objektive Zahlen / Subjektive Wahrnehmung Kontrolle Messung / Testing / Monitoring / SEO Einfluss

Slide 17

Slide 17

Aspekte von Aus Usersicht Website schnell / schlank / ruckelt nicht Performance Browser / Technologien HTML / CSS / Bilder / JavaScript / Schriften / Video / Audio Build / Server Bildformate / Komprimierung / CDN, Cloud / Caching / Netzwerk Wirkung Objektive Zahlen / Subjektive Wahrnehmung Kontrolle Messung / Testing / Monitoring / SEO Einfluss

Slide 18

Slide 18

Her mit dem Code !

Slide 19

Slide 19

lama.thomaspuppe.de

Slide 20

Slide 20

Los gehts ! https://lama.thomaspuppe.de/notes.html 1– Wir setzen eine lokale Kopie auf unserem eigenen Rechner auf. 2– lokal öffnen, oder lokaler Webserver, oder Netlify/Vercel 3– Debugging mit dem Chrome Browser

Slide 21

Slide 21

erster Überblick

Slide 22

Slide 22

Devtools im Browser Der Webbrowser mit seinen Developer Tools ist das allerbeste Werkzeug für Web-Developer.

Slide 23

Slide 23

Alarmsignale: • Caching • Komprimierung

Slide 24

Slide 24

Bildoptimierung Größe in MB 55 Original 14.9 Breite 1000px Optimierte PNG 3.5 WEBP 0.867

Slide 25

Slide 25

Bildoptimierung Bildoptimierung, schnell und simpel mit Node Modulen. Originalbilder 55,5 MB npx image-cli-tools ./images/.png maxwidth=1000 npx image-cli-tools ./images/.png -o npx image-cli-tools ./images/*.png -webp Quasi jedes Tool lässt sich manuell aufrufen oder in den Build-Prozess integrieren. Außerdem gibt es kostenlose Web-Services und Applications mit GUI. Das gilt selbst für SVGs (SVGOptimize).

Slide 26

Slide 26

Takeaway Absolute Basics: - Bildkomprimierung beim Build - Datentransfer mit gzip / brotli - Caching

Slide 27

Slide 27

WebP Browser Support

Slide 28

Slide 28

WebP Fallback Einbindung mit Fallback für ältere Browser: <picture> <source srcset=”hero.webp” type=”image/webp”> <source srcset=”hero.png” type=”image/png”> <img src=”hero.jpg” alt=”Lamas!”> </picture> Responsive Bilder und Retina lassen wir heute außen vor.

Slide 29

Slide 29

BildOptimierung Coding Time ☐ Bildpfade ändern von images_unoptimized zu images_optimized Credit: Pitch Sticker “Cyberpunk”

Slide 30

Slide 30

Credits: apple.com, Lee Campbell auf Unsplash, Shiwa ID auf Unsplash

Slide 31

Slide 31

Browser Network Throttling Chromium und Firefox haben in den Devtools die Funktion, eine langsamere Netzwerkverbindung zu simulieren und das Caching auszuschalten.

Slide 32

Slide 32

Credits: Walt Disney Animation Studios / Giphy

Slide 33

Slide 33

Takeaway 80% der Performance-relevanten Beobachtungen mache ich im Netzwerk-Panel der Browser Developer Tools.

Slide 34

Slide 34

Was wird wann geladen?

Slide 35

Slide 35

Hero-Image

Slide 36

Slide 36

Die Spalten im Netzwerk-Tab der Developer Tools lassen sich anpassen. So kann man zum Beispiel den Initiator von Requests anzeigen.

Slide 37

Slide 37

Critical Rendering Path im Browser Credit: https://www.youtube.com/watch?v=ufCVTowBxoY

Slide 38

Slide 38

Preload​ Mit Preload-Links können wir im Head einer HTML-Seite dem Browser schon Hinweise geben auf Ressourcen, die während dem Rendern gebraucht werden.

<head> <link rel=”preload” as=”image” href=”/images_optimized/png/hero.png”> {… CSS usw usf …} </head> <body> … Unterstützt werden nicht nur Bilder, sondern auch Videos, JS Dateien, CSS Dateien.

Slide 39

Slide 39

Preload Coding Time ☐ Gib dem Browser das Hero-Image schon im HTMLHead bekannt

<link rel=”preload” as=”image” href=”/images_optimized/png/hero.png”> Credit: Pitch Sticker “Cyberpunk”

Slide 40

Slide 40

Hero-Image mit rel=preload

Slide 41

Slide 41

Developer Tools:​ Resource Priority Im Netzwerk-Tab kann man mit der rechten Maustaste die dargestellten Spalten umstellen. Eine davon ist die Priority. Sie zeigt an, ob der Browser dieselbe Vorstellung von Resource Priority hat wie ihr.-

Slide 42

Slide 42

rel=preload Browser Support

Slide 43

Slide 43

rel=preload und Media Queries, Retina

<link rel=”preload” as=”image” href=”hero-narrow.png” media=”(max-width: 600px)” > <link rel=”preload” as=”image” href=”/images/hero-huge_2x.png” media=”(min-width: 900px) and (-webkit-mindevice-pixel-ratio: 2)”> <link rel=”preload” as=”image” href=”hero.jpg” imagesrcset=”hero_800px.jpg 800w, hero_1600px.jpg 1600w” imagesizes=”50vw”> <link rel=”preload” as=”image” imagesrcset=”hero.png 1x, hero-2x.png 2x”>

Slide 44

Slide 44

Takeaway Bilder und andere Ressourcen, die im ersten Viewport gezeigt werden, aber nicht im HTML vom Browser gefunden werden, per rel=preload im Head vorladen!

Slide 45

Slide 45

Während Preload Ressourcen auf der aktuellen Seite priorisiert, können mit Prefetch Inhalte vorgeladen werden, die auf späteren Seiten benötigt werden. Prefetch <link rel=”prefetch” as=”image” href=”/images/checkout-hero.jpg” /> <link rel=”prefetch” as=”document” href=”next-step.html” /> Das funktioniert für HTML-Seiten Bilder, Videos, Styles, JS. Es verbraucht Datenverkehr, sollte also bewusst und gezielt eingesetzt werden. User können es in den Browser-Settings unterbinden. “Quicklink” und “Guess” sind JavaScript Bibliotheken, die das Prefetchen automatisieren.

Slide 46

Slide 46

Lazy Loading

Slide 47

Slide 47

Lazy Loading Große Ressourcen sollten erst geladen werden, wenn sie tatsächlich gebraucht werden. Zum Beispiel: Bilder erst beim Scrollen auf der Seite laden.

Slide 48

Slide 48

Lazy Loading

Slide 49

Slide 49

Lazy Loading via JavaScript​ Für eine einfache Lazyload Implementierung reicht aus: <img data-src=”hero.png” alt=”Lamas!” > Dieses JavaScript tauscht die Bilder aus, sobald sie in den Viewport kommen: const images = document.querySelectorAll(‘img[data-src]’); const options = { rootMargin: ‘0px’, threshold: 0.5 }; const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const image = entry.target; const src = image.getAttribute(‘data-src’); image.setAttribute(‘src’, src); image.removeAttribute(‘data-src’); observer.unobserve(image); } }); }, options); images.forEach(image => { observer.observe(image); });

Slide 50

Slide 50

Lazy Loading Für eine einfache Lazyload Implementierung reicht aus: <img data-src=”hero.png” alt=”Lamas!” > via JavaScript​ Dieses JavaScript tauscht die Bilder aus, sobald sie in den Viewport kommen: const images = document.querySelectorAll(‘img[data-src]’); const options = { rootMargin: ‘0px’, threshold: 0.5 }; const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const image = entry.target; const src = image.getAttribute(‘data-src’); image.setAttribute(‘src’, src); image.removeAttribute(‘data-src’); observer.unobserve(image); } }); }, options); images.forEach(image => { observer.observe(image); });

Slide 51

Slide 51

Lazy Loading Für eine einfache Lazyload Implementierung reicht aus: <img data-src=”hero.png” alt=”Lamas!” > via JavaScript​ Dieses JavaScript tauscht die Bilder aus, sobald sie in den Viewport kommen: const images = document.querySelectorAll(‘img[data-src]’); const options = { rootMargin: ‘0px’, threshold: 0.5 }; const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const image = entry.target; const src = image.getAttribute(‘data-src’); image.setAttribute(‘src’, src); image.removeAttribute(‘data-src’); observer.unobserve(image); } }); }, options); images.forEach(image => { observer.observe(image); });

Slide 52

Slide 52

Lazy Loading Für eine einfache Lazyload Implementierung reicht aus: <img data-src=”hero.png” alt=”Lamas!” > via JavaScript​ Dieses JavaScript tauscht die Bilder aus, sobald sie in den Viewport kommen: const images = document.querySelectorAll(‘img[data-src]’); const options = { rootMargin: ‘0px’, threshold: 0.5 }; const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const image = entry.target; const src = image.getAttribute(‘data-src’); image.setAttribute(‘src’, src); image.removeAttribute(‘data-src’); observer.unobserve(image); } }); }, options); images.forEach(image => { observer.observe(image); });

Slide 53

Slide 53

Lazy Loading nativ Natives Lazyloading im Browser: <img src=”hero.png” alt=”Lamas!” loading=”lazy” >

Slide 54

Slide 54

Lazy Loading nativ Natives Lazyloading im Browser: <img src=”hero.png” alt=”Lamas!” loading=”lazy” >

<iframe src=”datawrapper.html” loading=”lazy” >

Slide 55

Slide 55

Lazy Loading nativ Natives Lazyloading im Browser: <img src=”hero.png” alt=”Lamas!” loading=”lazy” >

Slide 56

Slide 56

loading=lazy Verbreitung: 25% der Seiten im Netz nutzen das loading=lazy Attribut an Bildern. Credit: https://httparchive.org/reports/state-of-images#imgLazy

Slide 57

Slide 57

Lazy Loading Mit loading=lazy übergibt man die Kontrolle an den Browser. Und der macht vielleicht Dinge, die man nicht möchte. nativ Zum Beispiel wird mehr vorgeladen, wenn die Internetverbindung langsam ist! kein Throttling: 2 Bilder fast 3G: 7 Bilder slow 3G: 16 Bilder

Slide 58

Slide 58

Lazy Loading Coding Time ☐ Implementiere Lazy Loading mit data-src und js/lazyload.js — oder —​ ☐ Implementiere Lazy Loading mit load=lazy Credit: Pitch Sticker “Cyberpunk”

Slide 59

Slide 59

Pause Credit: Pitch Sticker “Cyberpunk”

Slide 60

Slide 60

Layout Shift

Slide 61

Slide 61

Layout-Shift​

Slide 62

Slide 62

Cumulative Der Cumulative Layout Shift (CLS) ist eine Maßeinheit für unerwartetes Verschieben des Layouts einer Website. Layout Shift CLS ist einer der Core Web Vitals von Google und fließt mit in das Google-Ranking ein! Credit: https://web.dev/cls/

Slide 63

Slide 63

Platz freihalten gegen Wer kennt den Padding-Trick? .image-container { position: relative; Layout-Shift​ padding-bottom: 56.25%; /* 16:9 Format, 9/16*100 */ } .image-container img { position: absolute; } Der Container des Bildes bekommt einen Padding-Bottom entsprechend des Bildverhältnisses. Das funktioniert, weil sich ein prozentuales Padding an der Breite des Containers orientiert. In den entsprechend dimensionierten Container passt das Bild (absolut positioniert, um nicht unter dem Padding zu landen) genau rein.

Slide 64

Slide 64

Platz freihalten gegen Layout-Shift​ Und wer kennt aspect-ratio? .img { aspect-ratio: 1.777; /* aspect-ratio: 16/9; */ } aspect-ratio definiert für Box-Elemente automatisch die Höhe anhand der Breite. Der Wert ist eine Dezimalzahl, die man aber auch durch einen Bruch angeben kann. Dieser wiederum nimmt auch die width/height Attribute eines Bildes entgegen.

Slide 65

Slide 65

Platz freihalten Und wer kennt aspect-ratio? gegen .img { Layout-Shift​ } aspect-ratio: 16/9; aspect-ratio definiert für Box-Elemente automatisch die Höhe anhand der Breite.

Slide 66

Slide 66

Cumulative Layout Shift Coding Time ☐ Binde das Hero-Image als Figure ein (statt als Hintergrundbild) und lade die kleinere CSS-Datei statt “styles-large.css”. ​ ☐ Definiere eine aspect-ratio von 1.777 für die Bilder ☐ Beobachte den Layout Shift (vorher und) nachher. ​ ☐ ggf Lazy Loading​ Credit: Pitch Sticker “Cyberpunk”

Slide 67

Slide 67

Takeaway Elemente, deren Höhe der Browser noch nicht kennt (aber ihr) mit aspect-ratio freihalten lassen, um Layout Shift zu vermeiden.

Slide 68

Slide 68

Core Web Vitals Web Vitals sind Metriken, die Google als zentrale Werte für die Performanve von Websites ermittelt. Diese Werte fließen ins Page Ranking mit ein. • LCP (Largest Contentful Paint): Zeit vom Klick auf einen Link bis zum Rendern des größten Teils des Inhalts. • CLS (Cumulative Layout Shift): optische Instabilität der Seite, gemessen aus Positionsverschiebungen von Elementen. • FID (First Input Delay): Verzögerung zwischen der ersten Interaktion mit er Seite, und einer Reaktion.

Slide 69

Slide 69

Pause Credit: Pitch Sticker “Cyberpunk”

Slide 70

Slide 70

Font Loading

Slide 71

Slide 71

Google Fonts @import url(‘https://fonts.googleapis.com/css2? family=Montserrat’); h1 { font-family: ‘Montserrat’; }

Slide 72

Slide 72

Google Fonts

<head> <link rel=”preconnect” href=”https://fonts.googleapis.com”> <link rel=”preconnect” href=”https://fonts.gstatic.com” crossorigin> <link href=”https://fonts.googleapis.com/css2? family=Montserrat” rel=”stylesheet”>

Slide 73

Slide 73

Selfhosted @font-face { font-family: ‘Montserrat’; Fonts font-style: normal; font-weight: 400; font-display: block; src: url(‘/fonts/Montserrat-Regular.woff2’) format(‘woff2’); } h1 { font-family: ‘Montserrat’; }

<link href=”/fonts/Montserrat-Regular.woff2” rel=”preload” as=”font” crossorigin>

Slide 74

Slide 74

Exkurs: Größe von Webfonts reduzieren 01 02 woff2 als Format verwenden Subsetting auf die benötigten Glyphen https://cloudconvert.com/ttf-to-woff2 https://everythingfonts.com/subsetter https://www.zachleat.com/web/glyphhanger/ 198 200 150 100 61 50 13 0 ttf Format woff2 Format woff2 Latin Subset 🤩

Slide 75

Slide 75

Font Preload​ Coding Time ☐ Optimiere die Einbindung der Schrift: ​ ☐ Self Hosting (lokale “MontserratLocal” verwenden) ​ ☐ kurzer “Critical Rendering Path” mit rel=preload ​ ​ ☐ Achtung: Google Font entfernen.​ Credit: Pitch Sticker “Cyberpunk”

Slide 76

Slide 76

Takeaway Schriften im woff2 Format selbst hosten und im HTML Head mit einem rel=preload ankündigen! Pro-Tip: Font Subsetting! ​

Slide 77

Slide 77

Font Loading / Display

Slide 78

Slide 78

… und während dem Laden der Webfonts?

Slide 79

Slide 79

… und während dem Laden der Webfonts? Credit: Zach Leatherman: The Mitt Romney webfont problem

Slide 80

Slide 80

Credits: https://www.fasterize.com/en/blog/web-performance-optimising-loading/

Slide 81

Slide 81

font-display Die font-display CSS-Property an @font-face Definitionen gibt dem Browser die Ladestrategie für Webfonts vor.​ @font-face { font-family: ‘MyWebFont’; src: url(‘myfont.woff2’) format(‘woff2’); font-display: [auto|block|swap|fallback|optional]; }

Slide 82

Slide 82

font-display Die font-display CSS-Property an @font-face Definitionen gibt dem Browser die Ladestrategie für Webfonts vor.​ @font-face { font-family: ‘MyWebFont’; src: url(‘myfont.woff2’) format(‘woff2’); font-display: [auto|block|swap|fallback|optional]; } Credit: https://caniuse.com/css-font-rendering-controls

Slide 83

Slide 83

font-display font-display: block; font-display: swap; font-display: fallback; font-display: optional; block swap fallback optional Platz wird freigehalten, System-Font wird Platz wird kurz freigehalten, Wie Fallback, aber der bis die Webfont angezeigt, bis die dann wird Systemfont Browser darf die Webfont geladen ist. Webfont geladen ist. gezeigt, dann die Webfont auch weglassen bei (wenn binnen 3s geladen). langsamen Verbindungen.​ Beim nächsten Seitenaufruf Beim nächsten Seitenaufruf ist die Schrift dann da. ist die Schrift dann da. FOIT FOUT (flash of invisible text) (flash of unstyled text) auto ist der Browser-Default, und das ist in der Regel swap/fallback. Swap wird auch von Google Fonts mitgegeben.

Slide 84

Slide 84

font-display Coding Time ☐ Benutze die langsame ‘MySlowWebfont” auf unserer Testseite. ​ ☐ Beobachte und ändere die Font Loading Strategie ​ ☐ Welche Strategie ist am besten gegen Layout Shift? Credit: Pitch Sticker “Cyberpunk”

Slide 85

Slide 85

Tooltipp Langsame Verbindungen simulieren mit • https://slowfil.es/ • https://www.npmjs.com/package/deelay Timeouts simulieren mit • https://blackhole.webpagetest.org • Blackhole-IP in die /etc/hosts eintragen: 72.66.115.13 www.google-analytics.com

Slide 86

Slide 86

Exkurs: Bei font-display: swap/fallback gibt es einen Flash of Unstyled Text (FOUT). Optimierung der Fallback Systemschrift Durch die unterschiedliche Laufweite und Buchstaben-Höhe von Systemschrift und Webfont springt der Text beim Laden der Webfont.

Slide 87

Slide 87

Exkurs: Bei font-display: swap/fallback gibt es einen Flash of Unstyled Text (FOUT). Optimierung der Fallback Systemschrift Mit dem Font Style Matcher von Monica Dinculescu kann man seine Fallbackfont per CSS so tweaken, dass der Flash of Unstyled Text möglichst unauffällig ist.

Slide 88

Slide 88

Vielleicht kannst du ganz auf Webfonts verzichten. Die Standard-Schriften der Betriebssysteme sind vielfältig. System Font Stacks • https://modernfontstacks.com/ • https://gist.github.com/don1138/5761014

Slide 89

Slide 89

Takeaway

  • Webfont benötigt? - Schriften im woff2 Format selbst hosten - im Head mit einem rel=preload ankündigen - Ladestrategie via font-display​

Slide 90

Slide 90

Performance Measuring Tools

Slide 91

Slide 91

Developer Tools:​ Code Coverage https://devtoolstips.org

Slide 92

Slide 92

Lighthouse Google Lighthouse ist ein kostenloses Open-Source-Tool von Google, das Webentwicklern dabei hilft, die Leistung, Zugänglichkeit, Best Practices und Suchmaschinenoptimierung (SEO) ihrer Websites zu bewerten und zu verbessern. Lighthouse führt automatisch eine Reihe von Tests durch, um festzustellen, wie gut eine Website in diesen Bereichen abschneidet, und gibt dann Empfehlungen zur Verbesserung der Ergebnisse aus. • in den Browser Devtools • CLI • 🎯 npm install -g lighthouse) npx lighthouse ( http://localhost:4321/index.html • Pagespeed Insights Website https://pagespeed.web.dev/

Slide 93

Slide 93

Webpagetest .org 🥰 Bestes Tool zur manuellen Überprüfung. Bietet gute Übersicht für Einsteiger, und eine Menge Profi-Features. ​ Gratis-Account verfügbar.

Slide 94

Slide 94

Speedcurve Bestes Tool für kontinuierliches Monitoring. Bietet neben synthetischen Tests auch Real User Monitoring. .com​ Nur Bezahl-Accounts, startet bei 12$ pro Monat.

Slide 95

Slide 95

🚀 Quellen zum Reinnerden ins Thema • web.dev/fast , awesome-wpo , • Blogs von CalibreApp , Harry Roberts , Tim Kadlec , Smashing Magazine , DebugBear , perfplanet.com • perf-tooling.today

Slide 96

Slide 96

🚀 Dankeschön​ https://www.thomaspuppe.de https://webperf.social/@thomaspuppe​

Slide 97

Slide 97

Timing verpeilt? ​ 1– Witzige Lama-Bilder generieren 2– Was fiel euch noch auf auf der Lama-Website? 3– Eine Website eurer Wahl analysieren 4– Fragen oder freie Diskussion zu Website Performance 5– fetchpriority, Responsive Images, JS/CSS Async Loading