Шырэй растлумачэнне Angular за дапамою меньшай колькасці работы: дзесят прыемаў, якія выходзяць за межы OnPush і trackBy
У сучасным Angular можна скорачыць непатрэбную роботу сеті, реакtyўныя операцыі, DOM і CPU за дапамою @defer, поступовага заполнення дадзеных, функциі resource(), вычысляваных сигналоў, фазаў адрасавання і Web Workers.
Крыніцы: How to Make Angular Extremely Fast: 10 Performance Tricks Most Developers Miss
Рэнтайм Angular рэдкасцю ёстся прычыной таго, што аплікацыя на Angular выглядае медленной. Частае прычыны — це калі аплікацыя завантажае, ініцыязавае, перасчыляе та атрыбутуе данні, якіх ніхто не прасіў. Звычныя способы рашэння проблем, такія як OnPush, ледзьвыя маршруты, trackBy, компіляцыя AOT, стварэнне версій для роботы ў працоўным серавере та меншая колькасць запытоў HTTP, яшчэ і далей дапамагаюць, але новыя версіі Angular даўаюць можлівасць горшай кантролі: калі завантажаецца код, калі компоненты становяцца інтэрактывнымі, калі абрываюцца запыты, што неяўнае робіць стан, калі выконваюцяся задачі з DOM та на якой нитку адбываюцца важкія вычысленні. У гэтым кансалтате практычна расказваецца пра дзесять тэхнік, створаных на адной з гэтых можлівасцей кантролі, пра тое, як іх спалучваць, а таксама пра тое, у якой спраўе іх застосаваць пасля таго, як вы вымерылі, куда на самай працоўны час ідзе.
Галоўная ідея простая: замест таго, каб робіць Angular быстрэйшым пад час непатрэбных задач, неабходна наладзіць так, каб гэтыя задачы вообща не адбываліся.
Дзе Angular-прыемка пасвячае свой час
Перш чым прыступаць да адзінаковых API, корыстна ўява пра тры ресурсы, якія викорыстоўвае кожная Angular-прыемка, і пра тое, што зазвычай спрацоўвае з кожным з іх.
Angular Application
│
┌────────────────┼────────────────┐
│ │ │
Network CPU DOM
│ │ │
JS downloads Change detection Layout
API requests Computation Paint
Dependencies Parsing Reflow
│ │ │
└────────────────┼────────────────┘
│
User Experience
Этыя витраты взаімаюцься. Налагоджэнне адказу на змены дае малай парадку, якщо пад час першага завантажэння стораніцы загружаецца 5 МБ коду JavaScript, а маленькі пакет не дапамагае, якщо кожная взаімадзейнасць запускае дорогі вычысленні. Мета — завантажваць менш, ініцыязаваць менш, выконваць менш вычыслэнняў, атрыбуець менш дадзеных і меньш блакаваць главныя поток. Дзесять тэхнік, прыведзеных нижэй, кожная нацелена на адну чы несколькі з гэтых задач.
1. Заставіць @defer ствараць сапраўдзіка аддзельныя часткі
@defer блакац яўляецца аднам з самых простых спосабоў скорачэння колькасці JavaScript-кода, які трэба запускаць пад час старту. Уявіце панель керування з графікамі, аналітыкай, звярненнямі, картамі, плеерамі відэа і элементамі адпаведальнага керування. Лячыны з іх не павінны існаваць адразу ж пасля ачынення сторанкі, пры тым чыстая шаблонна структура завантажае ўсе іх негайна:
<app-heavy-chart />
<app-map />
<app-report />
Абгортаванне важкага компонента ў блакац з адкладаннем паведамляе Angular запрашваць яго код толькі тады, калі выйдзе сигнал, у даным случае калі заместнік скроллуецца ў поле відображэння:
@defer (on viewport) {
<app-heavy-chart />
}
Не включаць компоненты з адкладаннем у імпорты типу barrel
Є адна тонкая прычына: @defer можа выдзеліць толькі той код, які ўжо не є доступным через негайны імпорт. Якщо гасподарскі компонент імпортуе компонент з адкладаннем через файл типу barrel, такі файл ператягвае ўсе іншыя элементы экспорту і яных залежнасці ў той самы граф. Таму замест таго, каб імпортаваць з файлу index:
import { HeavyComponent } from './index';
Імпортуйце компанент з ягонаго сабе файла:
import { HeavyComponent } from './heavy.component';
Мэтай — захаваць граф саўзалежнасцяў за кожным блакам з адкладаннем як магчыма ізольаваным. Першая форма, якой трэба ухіліцца, пазначаная так:
BAD
і ён выглядае так, калі адны барэль з’ѐеднае кожны віджет з панеллю керування:
Dashboard
│
└── index.ts
├── Chart
├── Map
├── Reports
├── Video
└── Analytics
Пярэйджаная форма пазначаная так:
GOOD
і гэта дае складнаму чарту прымусовы імпорт, а таму свой сабе чанк:
Dashboard
│
└── HeavyChart
│
└── heavy-chart.chunk.js
Імпорты чераз барэлі ўсё частаяя з адных з супакоўаных прычын, чаму блак @defer не можа зменшыць пачатковы бандл. Найпросты спосаб прызнаць успех — пераканацца ў выходных данных зборкі на наявнасць новага лэзі-чанка пасля дадавання блака.
Прыцэпны эфект ўсё большы: меньшы пачатковы пакет означае, што трэба завантажыць, апрацаваць, скомпіляваць і запусціць менша колькасць дадзеных, і весь гэты економія вырабляецца ўсё раней, чым Angular адразу паказвае ўсё на экране.
2. Хыдратацыя толькі таго, што спачатку патрэбна корыстнікам
Пры сервернай рэндарызацыі другі бутлнек пасляявання з’яўляецца пасля таго, як прыходзіт HTML. Сервер можа быстра стварыць маркап, але браузер пасля чаго должен зробіць усю сторанку інтерактыўной, што значыць завантажыць і запустіць код для кожнага з елементаў на ёй. Разглядзім типовую структуру панелі керування:
Dashboard
│
├── Header
├── Navigation
├── KPI Cards
├── Huge Chart
├── Map
├── Analytics Table
├── Video Player
└── Recommendations
Не всія з гэтых частак патрэбна адразу реагаваць на ввод. Класычная повная хыдратацыя спрацоўвае з сторанкай як з адной ейкавацей:
SSR HTML
↓
Hydrate EVERYTHING
↓
Interactive
Інкрементальная хыдратацыя дазволяе разбіць гэта на этапы, так што сторанка стае видна адразу, а часткі поступова становяцца інтерактыўнымі:
SSR HTML
↓
Show page immediately
↓
Hydrate only what is needed
↓
Hydrate other sections later
Это реалізуецца за дапамою тых сабе @defer блакоў, расшырэных прызначаннямі для запуску процеса гідратацыі. У наступным прыкладзе сервер все ўсё равна выкарыстоўваець HTML графіка, а кліент запускае процес гідратацыі, калі браузер не актыўны; у разе чыста кліентскай відраслі блако завантажваецца пад час взаімадзеяння з інтерфейсам:
@defer (on interaction; hydrate on idle) {
<app-heavy-chart />
}
Архітектурныя перавагі заключаюцца у тым, што процес выкарыстоўвання HTML і ўрабаткі з яго перестае быть адным, цэлысным крокам. Неабходна ўвімкнуць прызначання для поступовай гідратацыі у настройках; паказванае кераванне SSR для вашай версіі Angular мае точныя варыянты налаштавання.
3. Анулюйце застарэлыя запиты за дапамою resource()
Функцыя пошуку пад час набірання тэксту — класычны прыклад марнавання ресурсаў сеті. Калі корыстувач набирае тэкст, данні праходзяць чераз усі проміжныя станы:
a
an
ang
angu
angul
angula
angular
Простая реалізацыя може выклікваць запит для кожнага з іх:
Request 1 → "a"
Request 2 → "an"
Request 3 → "ang"
Request 4 → "angu"
Request 5 → "angul"
Request 6 → "angular"
Толькі последній адаптаванне мае значэння, таму іншыя пяць є марнымі. Сучасны Angular працюе з resource() і httpResource() для рэакtyўнага асінхроннага завантажэння. Спачатку стварываецца сігнал, які храніць запит:
const query = signal('');
Потым працэўваецца з ресурсам, у якога функцыя params чытае гэты сігнал, а loader выкананае завантажэнне. У прыведзенай нижчэй прыкладзе loader размешана на той жа лініі, што і замкнучыя складанкі params; гэтыя два элементы ўсё ж такі є аднальнымі атрыбутамі об’екта настройкаў:
const results = resource({
params: () => ({
q: query()
}), loader: ({ params, abortSignal }) =>
fetch(
`/api/search?q=${params.q}`,
{ signal: abortSignal }
).then(res => res.json())
});
Калі параметры змянююцца, пакуль завантажэнне ўсё яшчэ трывае, Angular абранавае пярэднее. Гэта можліва толькі таму, што loader перадае заданы сігнал у функцыю fetch; самэўажлівай лінія — гэта саме ёй:
{ signal: abortSignal }
Якщо гэта не зробіць, стары запит будзе продовжваць выконванне да канца. У рэальным коде варта дадаць два практычных паслегі: спачатку перакрыць запит функцыяй encodeURIComponent пры ўключэнні яго ў URL, а таксама памятаць, што анулювання запита не є методам debouncing. Кожны натык на клавішу все равно запускае запит, які пасля таго абрываецца; таму, якщо хочаце абэгчы ўсіх такіх запитоў, неабходна выкарыстаць сигнал з методам debouncing. Найшырэйшы запит — гэты, які абрываецца як толькі стане нерэлевантным, і такой падход є корыстным для пошуку, автодапамогі, фільтрацыі, жывых панелей керування, што стрэмкая змена ID-ў і ланцаў залежных вызоваў.
4. Храніце сигналы маленькімі і сфармаванымі
Сигналы є эфектывнымі, але іх можна выкарыстоўваць так, што губіцца гэтая эфектыўнасць. Часты падход — калі адны сигнал храніць стан цэлага сторынкі:
const pageState = signal({
user: {},
filters: {},
notifications: [],
products: [],
statistics: {},
settings: {}
});
Як толькі зміняюцца фільтры, паведамляецца кожны спожывач pageState, таму што цэлкавая значення сігнала было заменена. Раздзелэнне незалежных частак стану на адзінаковыя сігналы зменшае радыя ўплыву. Першы становіцца:
const user = signal(...);
а рэшта следуюць таму ж прыему (тут паказана як выпалюваная ў адной лініи; у рэальным коде кожны знаходзіцца на сваёй лініи):
const filters = signal(...);const notifications = signal(...);const products = signal(...);const statistics = signal(...);const settings = signal(...);
Тепер кожны спожывач залежыць толькі ад стану, які ён насправды чытае. Табліца є хорашым ментальным моделю. Калі змінюецца адна клетка:
A1 changes
вы не хачаце, каб весь лист занова быў вырахаваны, толькі ланцюг, який ад яго залежыць:
A1
↓
Dependent cells
↓
Only affected UI
Сігнал большога аб’екта выклікае недзейснае становішча для тых, хто заінтэресаваны толькі часткаюю ўсьго аб’екта; маленькія сігналы даюць вузкі дыапазон недзейснае. У замян на запитанне, чы можа весь стан поместіцца ў одны сігнал, трэба спытацца, што насправдзе павінна рэагаваць, калі змянюецца певная значэнне. Якщо вам даскладзіцца композытны аб’ект, створыце його за дапамою computed() на адной з маленькіх сігналоў, а не зберагайце як аднакова правда.
5. Вывядзіце дорогія вычысленні за межы шаблонаў
Выклік функцыі з шаблона ёсць аднам з найпростыях спосабоў стварэння павтараючыхся задач:
<div>
{{ calculateTotal(items()) }}
</div>
Це выглядае безнэбяжна, але Angular перацэнюе гэты выраз кожны раз, калі пераглядае відобразэнне, таму дорогая функцыя calculateTotal() запускаецца знова і знова, нават калі items не змянілася. Рашэння — использоваць сігнал computed(). Заявіце вучальны стан:
const items = signal<Item[]>([]);
Вычысціце аб’ём на ўзмацоўку з гэтага:
const total = computed(() =>
items().reduce(
(sum, item) => sum + item.price,
0
)
);
і прачытайце вычыслены сигнал у шаблоне:
<div>
{{ total() }}
</div>
computed() запам’ятоввае свой рэзультат і перычысляе яго толькі тады, калі змяніўся сигнал, які ён прачытаў, а не праз кожную перацэнку шаблону. Чырвона гэтае яго як ідальнае рашчыненне для фільтрацыі, сортавання, аграгацыі, статыстыкі, показнікаў на панелі керування і любога іншага вычысленага стану UI. Спіс продуктав, якія працэсаваны, мае той самы формат, выходзячы з сигнала запошуковага тэрміна:
const searchTerm = signal('');
і вычыслены спіс, які залежыць ад продуктав і тэрміна:
const filteredProducts = computed(() =>
products().filter(product =>
product.name.includes(searchTerm())
)
);
Гэта заменяе вызовы шаблонав, такіх як вышэй, перакладаючы работу ў реактыўную сетку:
{{ filterProducts(products(), searchTerm()) }}
Адзін даклэг: функцыя includes ўважае розлік верхніх і нижніх букв, таму практычны пошук зазвычай спачатку нормалізуе оба рэчэнні. computed() можна спачатку розглядаць як кэш для вырахаваных станоў, які правільна анулюе сабе.
6. Атрымлівая загрузка важкіх сервісаў, а не толькі компанентаў
Атрымлівая загрузка зазвычай обгаваруецца на двух роўнях:
Lazy route
Lazy component
Аднак сервісы можу мець свае вялікія дрэвы залежнасцей. Хорашым прыкладам є сервіс прадзірвання Markdown:
MarkdownService
│
├── marked
├── highlight.js
├── syntax definitions
└── other dependencies
Якщо сервіс выкарыстоўваецца толькі тады, калі хтось нажмае кнопку „Прадзірвання Markdown“, няма прычыны перадаваць парсер, падсвечвальнік сынтаксісу і ўсе яго граматыкі ў пачатковы пакет. Тэхніка заключаецца у атрымлівайй імпортацыі сервіса ў момент, калі яго патрэбна. Канцэптуальна це выглядае так, з викорыстаннем дапаможнікай injectAsync():
const markdownService = injectAsync(
() =>
import('../markdown.service')
.then(m => m.MarkdownService)
);
Потым компанент чакае на службу, якая знаходзіцца ў хэндлеры, якія яе патрабуюць:
async preview(content: string) {
const service = await this.markdownService();
і выкарыстоўвае яе для атракцыі кантэнту:
this.html = service.render(content);
}
Спрыяйце injectAsync() як шаблону, а не як гарантаванай основнай API: пераканаўцеся, чыя версія Angular або бібліятэка, якую вы выкарыстоўвайце, яе падтрымляе. Той самы эфект можна досягнуць за дапамогою простага await import('../markdown.service') у хэндлеры, пасля чаго клас разрешаецца через інжектар. У будзь-якім случае служба і яе складныя залежнасці падаюць у свой сабскрипт. Абщая правіла — завантажваць дорогія залежнасці там, дзе яны становяцься корыстнымі, а не толькі на межах маршрутаў.
7. Спрыяйце чытанню і запісу DOM як дорогай API
Інодзе вузькія месцы ўсё або не ў JavaScript, а ў дваржэце лейауту браузера. Гэта трапляецца, калі код перамешвае вимеры і мутацыі:
read DOM
write DOM
read DOM
write DOM
read DOM
write DOM
Кожны рэд пасля запісу можа змусіць браузер сінхронна перасчытваць лейаут, што называецца проблемай layout thrashing. Angular адказвае на гэта за дапамою хукіў, якія врачаюць процес рэндарування. Першы з іх:
afterNextRender()
А другі, які падтрымляе явныя фазы, ёсць:
afterRenderEffect()
За дапамою afterRenderEffect() вы размешчаеце заходы з вимерамі у фазе earlyRead:
afterRenderEffect({
earlyRead: () => {
return element.getBoundingClientRect();
},
А мутацыі — у фазе write, якая прымоўляе значэнне, вярнутае рэдам (гэты фрагмент ёсць часткай таго ж вызову TypeScript, а не CSS):
write: height => {
element.style.height = `${height}px`;
}
});
Angular выкананае фазы рэду всіх компонентаў першыя, чым фазы запісу, таму браузер бачыць, як работа групаваная такім чынам:
READ
↓
READ
↓
READ
↓
WRITE
↓
WRITE
↓
WRITE
У працоўку з постаямым алтэнатывным выкананням:
READ → WRITE → READ → WRITE
Такая коордынацыя, базаваная на фазах, мае вельмі важлівое значэнне для дыяграм, карт, анімацый, керування зменой размера і прасуваннем, бібліятэкаў UI ад трэціх сторон і складных панелей керування, дзе багато компонентаў зменяюць DOM пасля кожнага атрыбутавання.
8. Адкрыцья змян у пакетах за дапамою аб’ектаў здарэння і ўзьедынення выконання
У застосунках, базаванных на зонах, калькіляторы можаць запускацца адразу пасля аднаго логічна ўзаемаўз’язанага дзеяння корыстувальця:
click
↓
event bubbles
↓
parent handler
↓
ngZone.run()
↓
another update
Без узьедынення кожны крок можа планаваць свой сабстанвенны процес адкрыція змян. Angular можа ўз’едыніць іх, калі вы актываўце оба параметры ў provideZoneChangeDetection:
bootstrapApplication(AppComponent, {
providers: [
provideZoneChangeDetection({
eventCoalescing: true,
runCoalescing: true
})
]
});
У працоўку замест аднаго процесу на кожнае здарэнне:
Event A → Change Detection
Event B → Change Detection
Event C → Change Detection
Перакрытыя задачы зліваюцца ў адны процес:
Event A ─┐
Event B ─┼──→ One Change Detection
Event C ─┘
Гэта ўсё более корыстна ў аплікацыях з большой колькасцю обробнікаў запускоў, актываўными стрымамі WebSocket, частымі вызывамі ngZone.run() або высока інтэрактыўнымі панелямі керування. Памятайце, што гэтыя опцыі прызначаны для адкрыцья змян на базе Zone.js; новейшыя версіі Angular вяртаюцься да адкрыцья змян без Zone, дзе планаванне вже контролюецца сігналамі і явнымі паведамленнямі, таму пераканайцеся, які режым викорыстоўвае ваша аплікацыя, прытым як толькі прагучаеце ўжываць іх.
9. Знайсці залежнасці, якія вы не хацелі дадаць
Дзеяныя з найбольшых проблемаў з выдатнасцю — гэта не пробламы Angular, а пробламы з імпортам. Можа, вам патрэбна толькі адна функцыя з бібліятэкі, але структура імпорта можа запусціць завантажэння набагато большай колькасці матерыялу:
Application
│
└── Library
│
├── Feature A
├── Feature B
├── Feature C
├── Feature D
└── Feature E
калі на самай працэ вы викорыстоўвалі толькі:
Feature A
Самэ гэтая прычына, чаму аналіз пакетаў ўключаецца ў кожную пераглядку выдатнасці. Створыце статыстыку прадукцыйнага збору:
ng build --configuration=production --stats-json
Пасля чаго трэба аналізаваць выходныя даны, ўбачыць, якія файлы і бібліятэкі домінуюць у пакете даных. Толькі такія інструменты, як source-map-explorer, працуюць з картою выхадных кодаў, таму ўвімкніце іх у процес складання, які вы аналізаваеце; формат файлу з статыстыкай залежыць ад інструмента складання, які викорыстоўвае ваш проект, таму пераканайцеся, які аналізатор ўсё гэта падтрымляе. Важлівы не тое, чы робота прыложэння здаецца медленной, а самэўсё — што вы насправды надаеце браузеру. Калі гэта стане відчутна, большасць оптымаўзацый стане зрозумелай. Чырабець регрэсіям пасля чаго можна запобiec, разважыўшы застосаванне бюджэтаў на працоспроможнасьць у CI.
10. Перанесіце важкія вычысленні з галоўнага потока
Інодзе сама фрамворк працуе нормальна, а проблема ў алгорытме. Падазроўваючы, што панелі керування прымае набор дадзеных такога размеру:
100,000 records
і выконвае статыстыку, обробку зображэнняў, геаметрыю, большыя трансфармацыі, складную парсацію чы аграгацыю над ім у браузеры. На галоўнай вяліце гэтае выкананне конкуруе з усім іншым:
Main Thread
│
├── Angular
├── Rendering
├── User input
├── Animations
└── 100K-record calculation
Рэзультатам являецца проблемы з работай: застойны ввод, перыясткі анімацый і запазджанне у відрасліванні. Web Worker перамешчае вычыслення на аднойчынную вяліцу і прышлеў толькі рэзультат:
Main Thread Worker
│ │
│──── large dataset ─────>│
│ │
│ Calculate
│ Transform
│ Analyze
│ │
│<──── result ─────────────│
│
Render UI
Angular CLI можа стварыць такі Web Worker і налаштаўваць яго конфігурацыю:
ng generate web-worker app
а компанента стварае яго за дапамою URL, адносныя да модуля, каб бандлер могаў яго знайсці і сконструюваць:
const worker =
new Worker(
new URL('./app.worker', import.meta.url)
);
Рабочыя працоўнікі не робяць работу самага Angular дышэўней. Яны проста не дазволяюць дорогай логіцы аплікацыі блакаваць процес выканання і взаімадзею з корыстнікам. Іспользуйце рабочыя працоўнікі толькі тады, калі вычыслення справды складныя, адтаколькі даны, якія перадаюцца между нитямі, копіююцца за дапамою структураванага клонавання, і гэта перадача таксама мае свой расход. Чыба ўбачыць нейтральны погляд на гэтае рашэнне з точкі зоры фреймворка, адзірніце калі пераносіць работу CPU у Web Worker.
Спалучэнне тэхнік у адным панелі керування
Это не дзесяць адзінаковых трюкаў; яны падкрэпляюць адзін другога. Неоптымізаваная панель аналітыкі выкарыстоўвае ўсё заодно:
Browser
│
├── Download huge JS bundle
├── Initialize everything
├── Hydrate everything
├── Start every API request
├── Recalculate large state
├── Execute expensive template functions
├── Perform DOM reads/writes
└── Calculate large datasets on main thread
З прымененням гэтых тэхнік кожны слой выкарыстоўвае толькі тое, што трэба ў даны момент:
Browser
│
├── Smaller initial bundle
│ └── @defer
│
├── Partial interactivity
│ └── Incremental hydration
│
├── Only latest API request
│ └── resource/httpResource
│
├── Narrow reactive updates
│ └── Fine-grained signals
│
├── Cached derived values
│ └── computed()
│
├── Heavy services loaded later
│ └── injectAsync()
│
├── Coordinated DOM work
│ └── afterRenderEffect()
│
├── Fewer redundant CD cycles
│ └── Event coalescing
│
├── Smaller dependencies
│ └── Bundle analysis
│
└── Heavy computation elsewhere
└── Web Worker
Програма не толькі працюе быстрэй, але і выкарыстоўвае значна меншае количество роботы.
Компактная формула карыстоўнасці
Одным з спосабоў падсумавання гэтага падходу є сума зменшэнняў:
Performance
=
Less JavaScript
+
Less Network Work
+
Less Reactive Work
+
Less DOM Work
+
Less Main-Thread Work
Ёща адно стисла: швыракі Angular значыць выкарыстоўваць меншае количество роботы, выкананне яе пазней і толькі тады, калі гэта неабходна.
Практычны порядак оптымізацыі
Не трэба застосавляць усё адразу. Спачатку змеры, а потым працаваць па шарах прыблізна ў такім порядку, адколькі ранейшыя крокі зазвычай маюць найбольшы эфект на запуск.
Спачатку зменшычы пачатковы JavaScript:
@defer
injectAsync()
bundle analysis
Другае — зменшыць пачатковую роботу з гідратацыяй:
Incremental Hydration
Трэцяе — абмежыць непатрэбную сетавую роботу:
resource()
httpResource()
Чатвёртае — скорачыць некальківанне рэакtywnых элементаў:
Fine-grained signals
computed()
Пятае — зменшыць накладныя витраты на атрыбутаванне:
afterRenderEffect()
afterNextRender()
Шоста пункт: адзінуцьце зайвую пераканаўку змян:
eventCoalescing
runCoalescing
Сьведзьмая пункт: усунуць перашкоды ў работе CPU:
Web Workers
Зьмеры перш чым оптымацаваць
Найважнейшыя правіла — гэта утримацца ад слепай оптымацыі. Їе застосаванне всюды не ўважаецца стратэгіяй:
OnPush
І таксама не ўважаецца правільным внедрэнне якога-небудзь з гэтых рашэнняў без паведамлення пра прычыну перашкод:
memoization
workers
RxJS operators
defer blocks
Аналізуйце працэс за дапамойкай Chrome DevTools, Lighthouse, Angular DevTools, аналізу пакета для выпуску і панелі карыстоўчай спроможнасці прыгледача. Потым задайце базовыя пытанні:
What is actually slow?
І сконцентруйце адпаведныя адказы на конкрэтной катэгорыі:
Network?
JavaScript?
Change detection?
Rendering?
Layout?
CPU?
Толькі калі вы знаеце, якая з прычын ўплывае на працэс, трэба выбраць адпаведную тэхніку. Пасля кожных змян зноў зьмеры, южабаючы, чы рэальна павышэння эфектыўнасці.
Ад быстрэйшай пераканаўкі змян да меншага калібру работы
Старыя парады з працэйнасцю Angular большасцю былі спрямаваны на адзін вопыт:
"How can I make change detection faster?"
Сучасны Angular практыкуе более корыстны вопыт:
"Why am I running this work at all?"
Эты другі вопыт застосоўваецца да кожнага слоя. Чаму завантажваць гэты компонент зараз? Чаму ініцыялізаваць гэты раздзел зараз? Чаму чакаць, пакуль запыт HTTP завершыцца, калі корыстнік вялікі час яўна змёніў параметры? Чаму анулюваць цэлы об’ект стану зарады аднаго поля? Чаму пераскладваць значэнне, якога вхідныя даны не зменіліся? Чаму правіць змяны ў DOM прычынай таго, што рэндарынг яшчэ не завершыўся? Чаму выконваць тэжкія вычысленні на потоце UI? Кожная непатрэбная операцыя, якую вы знаходзіце, ёсць можлівасцю.
Ключовыя выводы
Дзесяць тэхнік нацеленыя на разныя слоі, і кожная з яных усунець адзін конкрэтны вид марнацькоў:
@deferу поўнасці з імпортамі зменшае пачатковы JavaScript-код.- Інкрементальная ініцыялізацыя зменшае пачатковую кантрольную роботу.
resource() з сігналам абаранцу выкарыстоўваецца для зняць застарэлыя сетавыя задачы.computed() запобегае павтаральным вычысленням.afterRenderEffect() запобегаюць частым перераспакоўкам макета.Найшырэйшая аплікацыя Angular — это не тая, якая выконвае JavaScript найшырэйша; гэта тая, якая выконвае наименьша колькасць непатрэбнага JavaScript. Уся стратэгія можа быць паказана ў адной картыне:
MAKE ANGULAR FAST
│
┌──────────┴──────────┐
│ │
DO LESS DO LATER
│ │
┌─────┼─────┐ ┌─────┼─────┐
│ │ │ │ │ │
Less Less Less Defer Lazy Hydrate
JS CPU DOM code data later
│ │ │ │ │ │
└─────┴─────┴─────────┴─────┴─────┘
│
▼
FASTER EXPERIENCE
Звярзаць меры, знайсці витрачаную роботу, адняць яе і знова звярзаць меры.