ЁЯПл The SchoolтА║ЁЯУМ UIтА║ЁЯЪА рдзрдбрд╛ 09 тАФ Performance: рдлрд▓рдХ рдкрдЯрдХрди рдЙрдШрдбрддреЛ
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯЪА рдзрдбрд╛ 09 тАФ Performance: рдлрд▓рдХ рдкрдЯрдХрди рдЙрдШрдбрддреЛ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 09 ┬╖ рдорд╛рдЧреАрд▓: lesson-08-design-tokens ┬╖ рдкреБрдвреАрд▓: lesson-10-testing


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ08, рдЖрдгрд┐ рддреНрдпрд╛рд╕реЛрдмрдд рдлрд▓рдХ рдордВрдж рдХрд╛ рд╡рд╛рдЯрддрд╛рдд рдЖрдгрд┐ рдЦрд░реЛрдЦрд░ рдХрд╛рдп рджреБрд░реБрд╕реНрдд рдХрд░рддреЗ: рдХрдореА bytes, рдкреНрд░рд╛рдорд╛рдгрд┐рдХ images, caching, рдЖрдгрд┐ рдорд╛рдгрд╕рд╛рд▓рд╛ рдЬрд╛рдгрд╡рдгрд╛рд░рд╛ рд╡реЗрдЧ рд╕рд╛рдВрдЧрдгрд╛рд░реЗ рддреАрди рдЖрдХрдбреЗ тАФ Core Web Vitals.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдлрд▓рдХ рдХрдВрдЯрд╛рд│рд╡рд╛рдгреНрдпрд╛ рдХрд╛рд░рдгрд╛рдВрдиреА рдордВрдж рдЕрд╕рддреЛ: рддреЛ рдЬрдб рдЕрд╕рддреЛ (download рдХрд░рд╛рдпрд▓рд╛ рдЦреВрдк рдХрд╛рд╣реА), рддреЛ рдерд╛рдВрдмрддреЛ (рдПрдХ file рдкреБрдврдЪреНрдпрд╛ file рд▓рд╛ рдЕрдбрд╡рддреЗ), рдХрд┐рдВрд╡рд╛ load рд╣реЛрддрд╛рдирд╛ рддреЛ рдЙрдбреНрдпрд╛ рдорд╛рд░рддреЛ. рдкреНрд░рддреНрдпреЗрдХрд╛рд╡рд░ рдПрдХ рдХрдВрдЯрд╛рд│рд╡рд╛рдгрд╛ рдЙрдкрд╛рдп рдЖрд╣реЗ:

рдордЧ рдорд╛рдгрд╕рд╛рд▓рд╛ рдХрд╛рдп рдЬрд╛рдгрд╡рддреЗ рддреЗ рдореЛрдЬрд╛ тАФ Core Web Vitals: LCP (рд╕рдЧрд│реНрдпрд╛рдд рдореЛрдареА рдЧреЛрд╖реНрдЯ рдХрд┐рддреА рд▓рд╡рдХрд░ рджрд┐рд╕рддреЗ), INP (taps рдирд╛ рдХрд┐рддреА рдкрдЯрдХрди рдкреНрд░рддрд┐рд╕рд╛рдж рдорд┐рд│рддреЛ), CLS (рдЧреЛрд╖реНрдЯреА рдХрд┐рддреА рдЙрдбреНрдпрд╛ рдорд╛рд░рддрд╛рдд). рддреБрдордЪреНрдпрд╛ laptop рд╡рд░ рдирд╛рд╣реА, рддрд░ throttled phone рд╡рд░ рдореЛрдЬрд╛.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    w["тЪЦя╕П weight<br/>minify ┬╖ compress ┬╖ split ┬╖ delete"]
    i["ЁЯЦ╝я╕П images<br/>right size ┬╖ webp/avif ┬╖ lazy ┬╖ width+height"]
    c["ЁЯзК caching<br/>hashed names + long max-age ┬╖ CDN"]
    m["ЁЯУП measure<br/>LCP ┬╖ INP ┬╖ CLS on a slow phone"]
    w --> i --> c --> m
    m -->|"the number that moved"| w

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рд╡реЗрдЧ рд╣реЗ рдЕрд╕реЗ рд╡реИрд╢рд┐рд╖реНрдЯреНрдп рдЖрд╣реЗ рдЬреЗ рд▓реЛрдХрд╛рдВрдирд╛ рдЗрддрд░ рдХреЛрдгрддреЗрд╣реА рд▓рдХреНрд╖рд╛рдд рдпреЗрдгреНрдпрд╛рдЖрдзреА рдЬрд╛рдгрд╡рддреЗ, рдЖрдгрд┐ рдХрд╛рд░рдг рд╕рд░рд╛рд╕рд░реА network рд╡рд░рдЪрд╛ рд╕рд░рд╛рд╕рд░реА phone рддреБрдордЪреНрдпрд╛ machine рдкреЗрдХреНрд╖рд╛ рдЦреВрдкрдЪ рдордВрдж рдЕрд╕рддреЛ. рдХреЛрдгреАрд╣реА рдХреЗрд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ рдЕрднреНрдпрд╛рд╕рд╛рдд load рдЪрд╛ рдкреНрд░рддреНрдпреЗрдХ рд╕реЗрдХрдВрдж conversions рдЧрдорд╛рд╡рддреЛ тАФ рдЖрдгрд┐ рд╢рд╛рд│реЗрдЪреНрдпрд╛ рдлрд▓рдХрд╛рд╡рд░, рддреЛ рдХрдВрдЯрд╛рд│реВрди рд╕реЛрдбреВрди рджреЗрдгрд╛рд░рд╛ рдкрд╛рд▓рдХ рдЧрдорд╛рд╡рддреЛ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

рдлрд▓рдХ рдХрдореА рдЧреЛрд╖реНрдЯреА рдЕрд╕рд▓реНрдпрд╛рдореБрд│реЗ рдЬрд▓рдж рдЖрд╣реЗ: рддреАрди files, system fonts, inline SVG avatars (image requests рдЕрдЬрд┐рдмрд╛рдд рдирд╛рд╣реАрдд), framework рдирд╛рд╣реА, build рдирд╛рд╣реА. рд╣реА рд╕рдЧрд│реНрдпрд╛рдд рд╕реНрд╡рд╕реНрдд performance рд░рдгрдиреАрддреА рдЖрд╣реЗ тАФ рдЖрдгрд┐ рдЦрд░рд╛ project рдЬрдб рдЭрд╛рд▓реНрдпрд╛рд╡рд░ рдЬреНрдпрд╛рдЪреНрдпрд╛рд╢реА рддреБрд▓рдирд╛ рдХрд░рд╛рдпрдЪреА рддреЛ рдкрд╛рдпрд╛.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 ui/mock_api.py
# 1) the honest baseline:
wc -c ui/index.html ui/styles.css ui/app.js     # ~9 KB total, uncompressed
# 2) Lighthouse: DevTools тЖТ Lighthouse тЖТ Mobile тЖТ Analyze. Note LCP, CLS, and the score.
# 3) feel a slow network: DevTools тЖТ Network тЖТ throttle to "Slow 4G", reload. Then Performance тЖТ CPU 4├Ч slowdown.
# 4) create CLS on purpose: remove width="40" height="40" from the card <img> in index.html, reload on Slow 4G тАФ
#    watch the cards shift as avatars arrive. Put them back.
# 5) create a long task: in the Console run  let t=Date.now(); while(Date.now()-t<800){}  and try clicking during it тАФ that is bad INP.

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

Lighthouse рдлрд▓рдХрд╛рд▓рд╛ рдЙрдЪреНрдЪ performance score рджреЗрддреЛ, рдЖрдгрд┐ LCP 2.5 s рдЪреНрдпрд╛ рдмрд░реАрдЪ рдЦрд╛рд▓реА рдЕрд╕рддреЗ. Image dimensions рдХрд╛рдврд▓реНрдпрд╛рд╡рд░ рдордВрдж connection рд╡рд░ layout рд╕реНрдкрд╖реНрдЯрдкрдгреЗ рдЙрдбреА рдорд╛рд░рддреЛ (рдЖрдгрд┐ CLS рд╡рд╛рдврддреЛ); рддреЗ рдкрд░рдд рд▓рд╛рд╡рд▓реНрдпрд╛рд╡рд░ рддреЗ рджреБрд░реБрд╕реНрдд рд╣реЛрддреЗ. Busy-loop рдореБрд│реЗ page рдЬрд╡рд│рдЬрд╡рд│ рдПрдХ рд╕реЗрдХрдВрдж рдкреНрд░рддрд┐рд╕рд╛рдж рджреЗрдд рдирд╛рд╣реА тАФ рд╣реЗрдЪ INP рдореЛрдЬрддреЛ.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

User рд▓рд╛ рдЬрд╕рд╛ рдЕрдиреБрднрд╡ рдпреЗрддреЛ рддрд╕рд╛ рддреБрдореНрд╣реА рдлрд▓рдХ рдореЛрдЬрд▓рд╛рдд, рдЖрдгрд┐ layout shift рдЖрдгрд┐ рдЕрдбрд▓реЗрд▓рд╛ main thread рдореБрджреНрджрд╛рдо рдШрдбрд╡реВрди рджреБрд░реБрд╕реНрддрд╣реА рдХреЗрд▓рд╛.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: Core Web Vitals browsers рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рддреВрди рдореЛрдЬрддрд╛рдд рдЖрдгрд┐ search ranking рд╕рд╛рдареА рд╡рд╛рдкрд░рд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд; рддреНрдпрд╛рд╣реВрди рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдореНрд╣рдгрдЬреЗ рд▓реЛрдХ рдЬреНрдпрд╛рдмрджреНрджрд▓ рддрдХреНрд░рд╛рд░ рдХрд░рддрд╛рдд рддреНрдпрд╛рдЪреНрдпрд╛рд╢реА рддреНрдпрд╛ рдЬреБрд│рддрд╛рдд. рдпрд╛ рдзрдбреНрдпрд╛рдд "рд╣реЗ рдордВрдж рд╡рд╛рдЯрддреЗ" рд╣реЗ рдХреГрддреА рдХрд░рддрд╛ рдпреЗрдгрд╛рд░рд╛ рдЖрдХрдбрд╛ рдмрдирддреЗ.

тПня╕П рдкреБрдвреЗ

рдкреБрдврдЪреНрдпрд╛ рдмрджрд▓рд╛рдд рдореЛрдбрдд рдЕрд╕реЗрд▓ рддрд░ рдЬрд▓рдж рдЖрдгрд┐ рд╕реБрдВрджрд░ рдЕрд╕рдгреЗ рдкреБрд░реЗрд╕реЗ рдирд╛рд╣реА: UI testing тАФ unit, component, end-to-end рдЖрдгрд┐ рддреБрдордЪреНрдпрд╛рдХрдбреЗ рдЖрдзреАрдЪ рдЕрд╕рд▓реЗрд▓рд╛ smoke test.

git checkout lesson-10-testing

ЁЯЪА Lesson 09 тАФ Performance: the board loads fast

ЁЯУН You are here: Lesson 09 of 12 ┬╖ Previous: lesson-08-design-tokens ┬╖ Next: lesson-10-testing


ЁЯУж What's in this branch

Lessons 01тАУ08, plus why boards feel slow and what actually fixes it: fewer bytes, honest images, caching, and the three numbers that describe speed as a person experiences it тАФ Core Web Vitals.

ЁЯзТ Explain like I'm 5

A board is slow for boring reasons: it is heavy (too much to download), it waits (one file blocks the next), or it jumps around while loading. Each has a boring fix:

Then measure what a person feels тАФ Core Web Vitals: LCP (how soon the biggest thing appears), INP (how quickly taps respond), CLS (how much things jump). Measure on a throttled phone, not on your laptop.

ЁЯЧ║я╕П Diagram

flowchart LR
    w["тЪЦя╕П weight<br/>minify ┬╖ compress ┬╖ split ┬╖ delete"]
    i["ЁЯЦ╝я╕П images<br/>right size ┬╖ webp/avif ┬╖ lazy ┬╖ width+height"]
    c["ЁЯзК caching<br/>hashed names + long max-age ┬╖ CDN"]
    m["ЁЯУП measure<br/>LCP ┬╖ INP ┬╖ CLS on a slow phone"]
    w --> i --> c --> m
    m -->|"the number that moved"| w

тЭУ What

ЁЯдФ Why

Because speed is a feature people feel before they notice any other one, and because the median phone on the median network is far slower than your machine. Every second of load costs conversions in every study anyone has run тАФ and on a school board, it costs a parent who gives up.

ЁЯФз How (in this repo)

The board is fast by having little: three files, system fonts, inline SVG avatars (no image requests at all), no framework, no build. That is the cheapest performance strategy there is тАФ and the baseline you compare against when a real project gets heavy.

ЁЯзк Try it

python3 ui/mock_api.py
# 1) the honest baseline:
wc -c ui/index.html ui/styles.css ui/app.js     # ~9 KB total, uncompressed
# 2) Lighthouse: DevTools тЖТ Lighthouse тЖТ Mobile тЖТ Analyze. Note LCP, CLS, and the score.
# 3) feel a slow network: DevTools тЖТ Network тЖТ throttle to "Slow 4G", reload. Then Performance тЖТ CPU 4├Ч slowdown.
# 4) create CLS on purpose: remove width="40" height="40" from the card <img> in index.html, reload on Slow 4G тАФ
#    watch the cards shift as avatars arrive. Put them back.
# 5) create a long task: in the Console run  let t=Date.now(); while(Date.now()-t<800){}  and try clicking during it тАФ that is bad INP.

тЬЕ Verify тАФ what you should see

Lighthouse gives the board a high performance score with an LCP well under 2.5 s. Removing the image dimensions makes the layout visibly jump on a slow connection (and CLS rises); restoring them fixes it. The busy-loop makes the page unresponsive for the better part of a second тАФ the thing INP measures.

ЁЯПБ What you just proved

You measured the board the way a user experiences it, and you caused and cured a layout shift and a blocked main thread on purpose.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: Core Web Vitals are field-measured by browsers and used by search ranking; more importantly they match what people complain about. "It feels slow" becomes an actionable number in this lesson.

тПня╕П Next

Fast and pretty is not enough if it breaks on the next change: testing UI тАФ unit, component, end-to-end and the smoke test you already have.

git checkout lesson-10-testing
тЖР Previousdesign tokensNext тЖТtesting

This page is the lesson's README from the lesson-09-performance branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.