Безблокіруючы запуск Angular: Завантажэнне настройкаў за дапамою rxResource
Дазнаецеся, чаму аплікацыяны метод APP_INITIALIZER, які выконвае блакаванне, заставляе корыстувачаў Angular апынуцца на порожняй сторонце, і як rxResource, аплікацыяны шэль та рашувальнікі маршрутаў, завантажаюць настройкі без яго.
Крыніцы: Stop Blocking Your App Load: Architecting APP_INITIALIZER with Signals
У корпаратыўных дапрацоўках на Angular зазвычай патрэбна вельмі большая колькасць элементаў прычыну першага корыстнага экрана: флажкі функцый, правыя прызначэння корыстніка (RBAC), тэма аб’екта-арендніка, кліент для збору тэлеметрычных даных. Традыцыйны спосаб ўсё гэта атрымаць — це APP_INITIALIZER, які вяртае обяцанне, і Angular не атрымляе нічога пакуль гэтае обяцанне не будзе выпалена. Чырвоні вы пазнаёме, сколькі гэта коштуе, і як трaktаваць канфігурацыю як рэактыўны стан за дапамогою rxResource, так што сторнка з’являецца негайна, а чакаюць толькі тыя часткі, якія практычна залежаць ад канфігурацыі.
Чаму блакуючы ініціялайзер шкодзіць
Класычная наладка рэгіструе фабрыку пад мульті-прадастальніком APP_INITIALIZER. Фабрыка вяртае функцыю, якая ператварае запит на канфігурацыю ў обяцанне, і фрэймворк не запускае корневы компонент пакуль гэтае обяцанне не будзе выпалена.
// The Blocking Legacy Pipeline
export function initializeConfig(configService: ConfigService) {
// Angular halts all execution until this Promise resolves
return () => configService.loadConfiguration().toPromise();
}
// In app.config.ts
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializeConfig,
deps: [ConfigService],
multi: true
}
]
Эфект вырабатваецца праз простыя арыметычныя вычысленні: якщо канцэльнік налашчэння запрашваецца прыблізна 600 мс, корыстнікі ў кожны раз, калі запускаецца аплікэцыя, бачаць порожнюю сторунку прыблізна 600 мс. Гэты затрымкі безпосередньа вплывае на показ першага контэнту, і медленны або нефункцыональны канцэльнік прыводзіць да таго, што аплікэцыя стае порожней або не працюе, без можлівасці паказаць кориснага сообщэння.
У гэтым фрагменте ёсць два старыя элементы. Функцыя toPromise() у RxJS ўжо не выкарыстоўваецца, яе заменіла firstValueFrom(), а новейшыя версіі Angular не выкарыстоўваюць токен APP_INITIALIZER, яго заменіла функцыя provideAppInitializer(). Пераканайцеся, што вы ведаеце інструкцыі для версіі Angular, якая у вас працюе. Аднак ніякі з гэтых змян не усувае проблему блакання, яны толькі зменяюць сынтаксіс.
Расследжванне налашчэння як реакtyўной залежнасці
Альтэрнатыўна можна перастаць спрыятаць канфігурацыю як «ворат» і начаць спрыятаць яе як дадзеныя, якіх чакае інтэрфейс. Семья методаў resource() у Angular адразу ж выражае гэта: асінхронная значэнне, чыя станы загрузкі і адзінакоў выказваюцца як сігналы.
Сэрвіс канфігурацыі, створанный на адной з базаў rxResource
Уместо таго, каб даць Angular обяву (promise), сэрвіс адзначае ресурс. rxResource берае ладчык, який вяртае Observable — у гэтым прыкладзе це вызов HttpClient — запускае яго пад час стварэння сэрвіса і выказвае value, isLoading і error як сігналы. Жадна частка праграмы не чакае на яго пад час запуску.
// The Non-Blocking Reactive Architecture
@Injectable({ providedIn: 'root' })
export class ConfigService {
private http = inject(HttpClient);
// Define a resource that doesn't block the bootstrap
configResource = rxResource({
loader: () => this.http.get<AppConfig>('/api/config')
});
// Expose as a signal for easy template consumption
get config() { return this.configResource.value; }
get isLoading() { return this.configResource.isLoading; }
}
Заўважкі: гетеры вяртаюць самы сигналы, таму іх вызываюць корыстувачы, як у configService.isLoading(). API-і ресурсаў развіваюцца з кожным новым выласкам (у новыя версіі опцыя rxResource называецца stream, а не loader), таму паследавайце назвы опцый вашай версіі Angular.
Работа з настройкамі, без якіх не можа работаць аплікацыя
Дзеярыя настройкі ўсё-такі неабходны, напрыклад, значэнні, якія трэба для аутентыкацыі. Не ўсё ж такі трэба блакаваць всю аплікацыю; існуюць два лепшых спосабу чакання.
Варыянт 1: спачатку адрасаваць шэл
Зазвычай гэты ўзьяўленне є найкращым. Шэл (загалоўны блок, навігацыя, макет) адрасоўваецца зараз жа, а блок керування @if паказвае скелет, пакуль ресурс не завершыць загрузку, пасля чаго прыемлівае керування рутэр.
<!-- Non-blocking, instant render -->
@if (configService.isLoading()) {
<app-skeleton-loader />
} @else {
<router-outlet />
}
Пользоватары атрыбуюць негараздзе мглева візуальная адпаведнае рэакцыю, а шаблон являецца падходячым месцам для адаробкі таксама. Дадзіце галузь для configResource.error() з кнапкай парадзей, яка вызывае reload() рэсурса; інакшыя неудачныя запиты завершаюцца адрасаваннем без налагоджэння.
Варыянт 2: рашувальнік маршрутаў
Якщо пэўны маршрут патрэбуе налагоджэння пры ўвядзенні ў роботу, рашувальнік обмежвае чаканне толькі гэтай навігацыёй, а не всім процесам запуску.
export const configResolver: ResolveFn<AppConfig> = () => {
return inject(ConfigService).configResource.value!;
};
// In app.routes.ts
{ path: 'dashboard', component: DashboardComponent, resolve: { config: configResolver } }
Будзьце абярожны з гэтым точным адканаленнем. configResource.value — это сігнал, і асэрцыя non-null толькі замовчвае кампайляр: рэзалвер негараздо вяртае сігнал без чакання запиту. Ёнколі трэба чакаць, рэзалвер павінен вярнуць ўсё тое, што будзе готава як толькі з’явяцца даны, напрыклад Observable, створаны з рэсурса, який фільтруе статус завантажэння, або прымітывы календар HttpClient, абгорнуты ў firstValueFrom().
Калі корыстна метадысцепцыя без блакання
- Шырэйша швайна першага адражэння. Браузер можа негараздо адражаць HTML-структуру та базавы CSS, заместо порожньага корэнтнага элемента, што павышае показнік First Contentful Paint і іншыя метрыкі, якія фіксуюць пошуковыя системы та панелі керування.
Є пахід, які трэба прыняць: кожны компонент, який чытае наладжэння, тепер должен караць стан „яшчо не завантажана“, альбо знаходзіцца за межам (код @if шэлу або рашытача), які гарантуе гэта. Для чыстага моделювання такіх станоў добра падходзіць разліковы альянс, як паказана ў замены флагаў завантажэння на альянс AsyncState.
Заключанне
- Ініцыялайзер, які вяртае обяву, прымушвае всю аплікацыю чакаць на самы сповольны запуск.
rxResourceператварае настройкі на сігналы значэння, завантажэння і памялкі, да якіх можа адказваць шаблоны.- Падзейнік аплікацыі з канструкцыяй
@ifёсць стандартным; рашыявач маршрута падходзіць для маршрутав, якія не можаць быць актывацыяны без дадзэнняў, за ўмовы што ён практычна чакае. - Застаўляйце ініцыялайзеры для задач, якія практычна должны быць завершаны прытаму, калі ўжо што-небудзь відображаецца, напрыклад, для рэгістрацыі глобальнага адпрацоўвальніка памялак.
Незалежна ад таго, чы вы выберазеце глобальны падзейнік чы рашыявачы на кожны маршрут, прынцып застаецца тым жа: ператварыце чаканне на відразлівы, локальны стан UI заместо порожней сторонкі.