Галоўная / Артыкулы / Зьвязак з мэтрамі раней: чаму ранняя оптымізацыя ускладнюе запуск аплікацый Next.js

Зьвязак з мэтрамі раней: чаму ранняя оптымізацыя ускладнюе запуск аплікацый Next.js

Паглядзіце, як прытарэя мемоізацыя, шырокія межы кліентаў і кэшы у формате стака дадаюць складнасці аплікацыям Next.js, і як падход, у яком раней выконуецца зьвязок з мэтрамі, дапамагае ўтримваць іх швайнасць.

2401 слоў

Сторанка Next.js запускаецца за 1,2 секунды, але хтось вырашае, што гэта занадта медленна, і пачынайцца цікл оптымізацыі яшчэ до таго, як хтось паглядзеў на прафіль. Чырэх месцацоў пазней у базе коду ёсць мемаізацыя паўсюду, дынамічныя імпорты, якія ніхто не тэставаў, калькі суперактываўных кэшоў і спецыяльныя шляхі атрыбутаў, і програма становіцца ўсё менш зрозумелай, чым была раней. У гэтым кяліку пояснюецца, чаму такі падход так распространены, якія саме прычыны гэтага і як заменіць іх на методыку, якая базуецца на даступных даных і дадае складнасць толькі тады, калі цэлком параджуецца тым цифрам.

Праўая памылка: складнасць раней за доказы

Кожная адаптация зазвычай выглядае разумна пад час адзінаковага перагляду коду. useMemo тут, кэш там, аддзельны пакет для компонента, які здаецца важкім. Проблема ў тым, што яна накапліваецца: кожны механізм дагаўляе новыя аспекты кэшавання, якія трэба зрозумець, новыя шляхі атрыбутавання, якія трэба дыбагаваць, ўсё больш параметраў, пра якія трэба думаць, і больш коду, прызначанага для пасібнення выконанню.

Таму неяўнае падазрэнне — гэта не нехватка адаптавання. Гэта ўведэнне новых механізмаў пры тым, калі яшчо не вядома, што працуе медленна, чаму гэта так і чы рашэнне паўністю змяніць тое, што бачыць корыстнік. Перш чым налаштаваць выконанне, неабходна зробіць систему достатнюю для спостэрэння, каб можна было яе змерыць.

Аналіза прыроды проблемы перш чым чаго-небудзь зменіць

Найпасуючыя версія гэтага ошибкі — это реакцыя на загальную думку, што працэздатнасць мае значэнне. Разработчык прыначына правіць код без аналізу, без выявлення «узкага месца» і без падтверджэння таго, чыякажуць корыстувальнікі самэй конкретнае.

Сімптамы знайомы:

  • useMemo і useCallback, якія викорыстоўваюцца для простых вычысленняў
  • Даданыя, якія ніколі не былі складнымі для запрашэння, адзначаюцца як тыя, для карэнтавання
  • Раздзеленне коду аплікуюцца раней, чым хтось пераканаўся, якія часткі коду є великімі
  • Абстракцыі, створаныя на аднойчынных прогнозах будучага навантажэння
  • Логіка атрыбутавання, якая расширвае умовы, каб ухіліцца ад атрыбутавання таго, што ніхто не мерыў

Часта прычына проблемы — гэта ўсё-такі неяе наявнасць. Абавесны роботы з парадкамі работы сайту пачынаюцца з дадзэнняў: часу загрузкі стораны, аналізу пакетаў дадзэнняў, запису інформацыі з прыстрою для аналізу React, дэталів сетевай актывацыі і чыстага адбору тых момантав, калі корыстнікі насправды чакаюць. Оптымізацыя павинна спрыяць рашэнню конкретных проблем, а не толькі базавацца на невялікіх падазрачаннях пра тое, што калі-небудзь можа стаць медленна.

Практычны прыем — запісаць показнік, які вы хочаце змяніць, і його ныяшнюе значэнне прытаму, перш чым займацца кодам. Якщо вы не можете назваць цяперашнюе значэнне, значы вы ўсё ўсё еща не гатовы змяніць реалізацыю.

Зазвычай проблема — гэта проста занадто многі JavaScript

Большасць проблем з медленнасцю фронтэнду не є таінным. Ад браузера запытваюць заваносіць, парсаваць, компіляваць і выкананні больш скрыптав, чым патрэбна сторане, і на телефонах середньяго класу кожны з гэтых крокаў є дужа трацячым.

Спакет канцэлерыі простага структуры можа таямніча збіраць калькольку бібліятэкаў анімацый, большы набор компонентав, моцны калектар стану, пакеты для стварэння графікав, зборкі інструментаў і логіку на боку кліента для функцыяй, якія моглі бы застацца на сервере. Ні адна з іх сама па сабе не выглядае трывожнаю. Але разам яны ствараюць вялікую кантыгу роботы, прычымай яна сторана зможа комфортна адпаведзець на ввод дадзеных. У такі моменты каманды часта пачынаюць старацца паспрабаваць падняць низькія показнікі Lighthouse, як бы для ўладзення з гэтым патрэбна была хыткая стратэгія.

Next.js ўжо задае сильныя стандартныя настройкі: серверная рэндарызацыя, аўтаматычны разбіець коду па маршрутах і модэль, якая спачатку працюе на серверы, пабудаваная на React Server Components. Гэтыя стандартныя настройкі втрачаюць большую частку свайго значэння, калі вялікія часткі прыемлівкі пераводзяцца ў браузер. Таму, прычымоўваючы да іншых тэхнік, пераканайцеся, сколькі коду вы адправляеце, якія залежнасці домінуюць у кожным маршруце і чы рэальна кожная з іх падтрымлівае сваю важычку. Багатыя прыемлівкі не патрабуюць болей сложнай оптымізацыі; ім патрабуецца менш JavaScript. Чыба глэбачэй разумець, што ўсё ж такі можа спамляць стварэнне сторанкі, кроме простага размеру пакета, адзірніце што на самай працэ спамляе прыемлівку.

Межы кліента, якія павольна паднімаюцца

Спадзейны спосаб раскрыць архітектурныя прынтэсы App Router — це пазначаць занадто много чынніка як код кліента. Хтось патрэбуе інтерактыўнасці глыбока ў структуре, ставяе "use client" на бацьку элемент, потым на дзеда, і ўскора компаненты, якія выводзяць толькі статычны маркап, чытаюць данні сервера або складаюць лейаут, стаюць частынай пакета кліента проста таму, што знаходзяцца нижэй за гэтым дырективам.

Гэта змена паўтараецца не толькі ў месцы выканання коду. Яна зазвычай значыць:

  • больш JavaScript-кода, які надаецца браузеру
  • больш работы па ініцыялізацыі перад тым, як сторанка стане інтерактыўной
  • дадатковы стан на боку кліента, які трэба кераваць
  • больш месца, дзе данні сервера і кліента можу стаць несінхроннымі

У гэтым ёсць іронія. Багатыя команды пераходзяць на сучасны Next.js самэўсёлкі для таго, каб атрымаць архітектуру, якая прыоритэтавае сервер, а пасля поступова перабудоўваюць додатак на адной сторанцы, які заснованы на кліентскай частцы і ад якога яны намагаліся адстаць.

Корыстны вопыт пад час проектавання: якая ў гэтым інтерфейсе мінімальная частка, якой дасправды патрэбны браузер? Кнопка «Падаражыць», выпадаючы список чы ўплывны поле можа патрабаваць стану кліента; карточкі, спісы і сторанцы, якія ўсё гэта абрамляюць, часта такога не патрабуюць. Якщо строго сахраняць граніцы і, калі можна, перадаваць контэнт, выробленаў на серверы, у кліентскія компоненты як ўсёе дзецейскія элементы, сервер можа выканаць больш задач, а інтерактыўныя часткі застануцца сфокусаванымі. Даўгастрачны выгода — меньшыя пакеты дадзеных і простейшая модель разумоўкі. Механізмы, якія стояць за гэтым, практычна расказаны ў стацыі пра тое, як React Server Components не падключаюць код у пакет дадзеных.

Як прыткая оптымізацыя робіць архітектуру кранглевай

Работа над выдатнасцю ператвараецца на проблему тыямоўкі, калі оптымізацыі прыходзяць быстрей, чым хтось можа паказаць, што яны дапамагаюць. Це часта пачынаецца з малага: компонент стае мемуізаваным, з’яўляецца ручна створаны кэш, додаёцца хук для адхілення ад рендара, пасля чыго з’яўляецца ўсё новы слой, каб падтрымаць адпаведнасць двух частак стану. У той момент ніч гэта не здаецца рызыкавым.

Чэрез калькі месцацоў база коду заповнюецца спецыяльнымі хукамі, чыя взаімадзеяння важка адстэпіць, правіламі недзейнасці, якія розумеюць толькі калькі чалавекоў, ланцюгамі мемуізаваных значэнняў, умовамі рендара, якія базуюцься на прыпусках, якія больш не ўсталяваныя, а таксама кодам сінхронізацыі, які існуе галоўная мерой таму, што якаясь ранейшая оптымізацыя гэта выклекала.

Это мае рэальныя наследкі. Процес адаптавання трывае дольшэй, для выклікання бяговых прычын трэба больш меркавання, а незначныя змены функцый насправды паўтараюць працэю механізмаў, якія спачатку былі створаны для прышвартавання. Правыя пытанні ў зв’язку з будзь-якай оптымізацыёю — не тое, чы растлумачвае яна показнікы эфектыўнасці, а тое, чы ўдосконаленне дастаткова великае, каб пакрыць архітектурныя затраты, якія яна прыносіць. Мета — стойкая эфектыўнасць: прыстанок, які быстра рэагуе, а чыё кождадзеннае кодаванне застаецца простым і зрозумелым, а не наполненым хітрамі для прышвартавання.

Вярбованая эфектыўнасць — гэта проблема UX

Інжынеры прывяжаны да таго, што можна точна вымерыць — мілісекунды, размах пакетаў, колькасць працэй адрасавання та ўсё такое. Корыстувачы ж спрыягаюцца да шырэйшага спектру аспектаў. Скорачэнне часу завантажэння староні на 100 мс малая дапамога, якщо навігацыя заплутаная, статусы завантажэння не даюць жадных сведчынак, элементы керування не реагуюць, макет зміняецца праз тое, што прыходзіць новы контент, а важлівая дзеянне не паказвае жадных знакоў таго, што была выканана.

Возьмім форму, якая запоўнюецца за два секунды. Скорачэнне часу обробкі на бакэндзе да 1,7 секунды — гэта справжняя тэхнічная перадача. Негайна адпаведь, блокаванне кнопкі, каб запобiec падвойнаму запоўненню, і чыстая інфармацыя пра прагрэс часта даюць набліжна лепшы досвяд, нават якщо час обробкі запытку застаецца тым жа, як і раней.

Это — разлік межа мераваным і спрыгваемым карэнтам работы. Людзі ацэнююць інтэрфейс па тым, чы реагуе ён на ўсё, што яны хочаць зрабіць, чы ўсё розумеюць і чы ён даўае вялікі адчутак стабільнасці пад час викорыстоўвання. Таму хорашая робота з карэнтам фронтэнду апыяўляецца як на дизайне взаімадзеяння, так і на внутрашніх механізмах атрыбуцыі. Такія інструменты, як пераходы у React, оптымістычныя апдэйты і станы скелета, належаць да таго ж набору інструментаў, як і аналіз пакетаў.

Кэшаванне дапамагае, пакуль ніхто не можа яго поясніць

Кэшаванне можа прынесці вялікія выгоды, калі система перестае павтараць дорогія заданні. Аднойчы калі команда больш не можа сказаць, якую версію дадзеных павінен бачыць конкретны корыстувальнік, з’яўляюцца проблемы.

Тыповая ситуацыя: стораніца становіцца швайна, а пасля з’яўляюцься застарэлыя даны. Зменяецца адназначны запис, і адна панель апоштановіцца, тады як іншая продаўжвае паказваць старую значэнне. Процес разработкі працуе правільна, але у рэальных умовах — няўсёлі. У ходзе розследавання выклікаюцца запитанні пра тое, який кэш выдаў адпаведзь, які слой стаў неактуальным і який запит створыў выходны результат.

Большыя прыкладныя програмы на Next.js робяць гэта ўсё простым, таму што паўтарна адналікаванне можа выконваліцца на багато роўней: у вашам сэрве, у кэшаванні дадзеных і маршрутаў фрэймворку, у адзінаковых вызовах fetch, у CDN, у браузеры і сервісах на заднім канцэ.

Дадаўшы ўсё новыя слоі без розумэння таго, як яны взаімаюцца, можна зменшыць час адпаведзення, але ў той жытак збільшыць колькасць станоў, у якіх можа знаходзіцца система. Згадайце, што стандартныя настройкі кэшавання у Next.js зменяліся ў разных версіях, таму пераканайцеся ў працэсе версіі, яку вы вядзеце, па актуальной дакументацыі, а не спалагайцеся на старэйшых інструкцыях.

Возможнасць аблікавання стану системы павінна быць прыоритэтам перад агрэсывным кэшаваннем. Для кожнага кэшаванага адпаведзення команда павінна мочыць адпавесці на такія запытанні:

  • звядзе гэта адпаведзенне
  • як дуго яно, як ожыдваецца, застанэцца чынным
  • што яго анулюе
  • шта выходзіць, калі анулюванне не выйшла

Кэш, які прыяўляе швальнасць системы, але робіць ўзорку ефектаў непрадбачальной, не ёсць безкомпромісным рашэнням.

Можа, сповольненне воўсе не ў React

Інодзе затрымкі стаюць виднымі толькі на фронтэндзе, а не там, дзе яны пачынаюцься. Калі відобразэнне трэба калькuluваць колька секунд, прычаму команда можа пачаць скорачваць калькуляцыі, запам’ятовваць компаненты чыста перархітектураваць стан кліента. Такія змены могу заўсёды захаваць калькuluванні браузера на калькuluле мілісэкунд, пакуль стораніца ўсё яшчэ чакае на запыт да базы дадзенаў трываласцю два секунды чы на канец, які вяртае значна большае прамеры дадзенаў, чым трэба для відобразэння.

Уявіце жыцёвы цикл такога запиту: браузер яго адправляе, ён праходзіць через мідлвэр і процедуры автарызацыі, сервер вызывае бэкенд або базу дадзенаў, пакет даных вяртаецца назад, і толькі тады React адрасавае елементы. Якщо большая частка часу працы выканана до падачы адпаведзення, то незначна прышвартованне апошнага крока мало дапамагае.

Тое ж сама стосуецца занадта вялікіх пакетаў даных, последовных сецыянарных запытоў, якія маглі б выканавацца паралельна, дорогіх пераказоў автарызацыі, перавантажаных служб і запытаў без індэксацыі. Нічога з гэтага не можна выправіць простым рэдзераваннем компонентаў рэдкей. Корыстны анаіз включае перагляд усьго запиту і выявленне таго, куды зникае час. Узгорнуткі не вярнаюцца ні до меж каманды, ні да таго, хто ў власнасці коду.

Простыя системы дольш час застаюцца быстрымі

Большынства швайных прыемоў у сабе не ўражаюць. Яны выкладваюць дасыць маленькія пакеты, задаюць чыстыя межы атрыбутаў візуалізацыі, зменшуюць колькасць даных кліента, запрашаюць даны прыгадна і маюць архітектуру, якую новы разработчык можа зразумець без неабяжнага аналізу складных тэхнічных рашэнняў.

Такая простата дапамагае, калі прыемок расту. Калі трыбуты перадачы даных ўсёвядомы, легка выявіць дорогія у операцыйных процесах задачы. Калі межы кліента скупыя, ясна, за шта адпаведае браузер. Калі правілы кэшавання малае і чыстае, проблемы пад час роботы прыемка легка діагноставаць.

Хыткая оптымацыя ўпадае ў вочы часткова таму, што паказвае тэхнічныя навыкі, але кожны механізм стае чымось, што будучыя разработчыкі должны зразумець, дыбагаваць, захаваць або зрэшты выдаліць. Нічыга з гэтага не спраўляецца проты оптымацыі. Гэта спраўляецацца на корысть самай простой рэалізацыі, якая задовольняе рэальныя трэбаванні да працызнасці, пры чым дадатковая складнасць додаецца толькі тады, калі памеры пакажуць, што простая конструкцыя выйшла за межы можлівасцей. Система, якая ўсё-такі менш хыткая, але набагато простейшая для разумэння, зазвычай працюе краща з часам.

Спрыяйце рэзультатам як сігналам, а не цялям

Інструменты для памераў працызнасці є цэннымі, таму што яны ператвараюць невидмыя характэрыстыкі на тые, якія можна адзірваць. Проблема пачынаецца тады, калі падвышэнне рэзультата стае важлівей, чым паспрабоўкі падбярэць продукт краща.

Высокія рэзультаты теста Lighthouse не гарантуюць хорашага дзеяння інтерфейсу, адмініструемайной архітектуры, надзеянага працавання у рэальных умовах чы то шырокага выканання задач, якія маюць значэнне для корыстувачаў. Замеры ў лабараторных умовах адбываюцца за контрольваныяя прыняцці; рэальныя корыстувачы вжываюць разныя прыстроі, сеті, об’ёмы дадзеных, статусы аутэнтыкаціі і шляхі навігацыі. Самэлькія даны, такія як Core Web Vitals, збіраныя пад час рэальных сесый, ўсё гэта яўляецца корыстным дапамогам самэлькія з гэтай прычыны.

Гэта не робіць показнікі з лабараторных тэстаў незначнымі; гэта зменяе спосаб іх вжытку:

  • Якщо показнік указвае на справжнюя проблему, трэба яе выявіць.
  • Якщо змена падымае рэзультат, але дагаўляе значную складнасць, пры чым корыстувачы практычна нічога не зазначаюць, трэба пераспакаваць ці карысна гэта рашэнне.
  • Кожны показнік трэба прыяўязваць да конкрэтнага дзеяння, яке бачыць корыстувач, і якое можна описаць.

Мэтаем не ўсё тое, што выглядае чудова ў тэстах на спроўвачы выконання. Мэтаем — аплікацыя, якая дазволяе людзям выканаць свою работу без непатрэбных затрымакоў чы працяглівасцей.

Рабочы процес, адзінакаваны з мерамі

Якшто адзінаваць гэтыя ідеі, стойкі цыкл выглядае так:

  1. Назваць проблему, з якой сталкаецца корыстнік, і паказначык, який яе адражае.
  2. Змяроўваць ныякія значэння як у лабараторных, так і, калі гэта можліва, у рэальных умовах.
  3. Аналізаваць весь шлях запыту, каб выявіць галоўную прычыну затрат.
  4. Спробаваць змяны, якія спачатку пазбавляюць ад працы: менш залежнасцей, вузкіяй межа кліента, меншым пакетам дадзеных, шырэйшым запытам.
  5. Апяроцаваць мемуізацыю, дадатковую кэшаванне чы спецыяльную обробку толькі тады, калі сама пазбавленне недастатня.
  6. Знову змяроўваць і застаўляць змяну толькі тады, калі практычны выгода перавышае ўскладнення з яе падтрымкай.

Мяжа кліента — на лісце, не на старонцы

Дырэктыва 'use client' на старонцы цягне загрузку даных у браўзерны бандл. Пакіньце старонку серверным кампанентам.

// app/dashboard/page.tsx — a Server Component, no 'use client'
import { Chart } from './chart';

export default async function Dashboard() {
  const points = await loadSeries();
  return <Chart points={points} />;
}

Мемаізуйце ліст толькі калі профіль пакажа, што дарагое менавіта маляванне.

'use client';

export const Chart = ({ points }: { points: number[] }) => {
  return <svg data-count={points.length} />;
};

Галоўныя выводы

  • Сярод самых дорогіх памылак у працэйных характарастых Next.js ёст тое, што оптымізацыя практыкуецца раней, чым разумеюцца дзеяння системы, а не тое, што забываюцца ведаць пра оптымізацыю.
  • Большая частка проблем з адтрымкай выходзіць з накопічанай складнасці: занадто великі колькасць коду на стороне кліента, шары кэшаў, можна было бы узбегнуць процесу гідратацыі і спекулятивныя абстракцыі.
  • Зазвычай кращэ адмовіцца ад дадзення новых механізмаў, чым іх дадаць — гэта можа значыць выкорыстоўваць менш JavaScript, заставляць компоненты працаваць на сервере, спроставаць стан дадзеных, адмовіцца ад зайвых запытоў, вылечыць медленныы запыт на бэкенде або выдаліць оптымізацыю, якая коштуе больш, чым зарабляе.
  • Дзякуючы некалькам прыемлень, дэйсна патрабуецца сложныя стратегіі кэшавання або рэндарынгу, але така рашынка павінна быць вынесена на адварота пасля зьмерэнняў і чыстага діагназу.
  • Найшвэдзейшая прыемлень — часто тая, якая выканае наименш зайвай работы.